thin-provisioning-tools/functional-tests/device-mapper/ioctl.scm

404 lines
12 KiB
Scheme

(library
(device-mapper ioctl)
(export dm-open
dm-close
with-dm-thunk
with-dm
dm-device
dm-device-name
dm-device-path
dm-device-minor
dm-device-major
dm-version
get-version
remove-all
list-devices
create-device
target
make-target
target-len
target-type
target-args
load-table
remove-device
with-empty-device-fn
with-empty-device
with-device-fn
with-device
with-devices
suspend-device
resume-device
clear-device
pause-device
pause-device-thunk
get-status
get-table
message
get-dev-size
discard)
(import (chezscheme)
(disk-units)
(fmt fmt)
(logging)
(srfi s8 receive)
(utils))
(define __ (load-shared-object "../lib/libft.so"))
(define (fail msg)
(raise
(condition
(make-error)
(make-message-condition msg))))
(define-ftype DMIoctlInterface
(struct
(fd int)))
(define-record-type dm-device (fields name major minor))
(define (dm-device-path d)
(fmt #f (dsp "/dev/dm-") (dsp (dm-device-minor d))))
(define open% (foreign-procedure "dm_open" () (* DMIoctlInterface)))
(define (dm-open)
(let ((ptr (open%)))
(if (ftype-pointer-null? ptr)
(fail "couldn't open ioctl interface (permissions?)")
ptr)))
(define dm-close
(foreign-procedure "dm_close" ((* DMIoctlInterface)) void))
(define dm-interface #f)
(define (current-dm-interface)
(if dm-interface
dm-interface
(fail "no dm interface")))
(define (with-dm-thunk thunk)
(fluid-let ((dm-interface (dm-open)))
(dynamic-wind
(lambda () #f)
thunk
(lambda () (dm-close dm-interface)))))
(define-syntax with-dm
(syntax-rules ()
((_ b1 b2 ...) (with-dm-thunk (lambda () b1 b2 ...)))))
(define-record-type dm-version (fields major minor patch))
(define (alloc-u32)
(make-ftype-pointer unsigned-32
(foreign-alloc (ftype-sizeof unsigned-32))))
(define (deref-u32 p)
(ftype-ref unsigned-32 () p))
(define (free-u32 p)
(foreign-free (ftype-pointer-address p)))
(define-syntax with-u32
(syntax-rules ()
((_ (v) b1 b2 ...)
(let ((v (alloc-u32)))
(dynamic-wind
(lambda () #f)
(lambda () b1 b2 ...)
(lambda () (free-u32 v)))))))
(define-syntax with-u32s
(syntax-rules ()
((_ (v) b1 b2 ...)
(with-u32 (v) b1 b2 ...))
((_ (v rest ...) b1 b2 ...)
(with-u32 (v)
(with-u32s (rest ...) b1 b2 ...)))))
(define (get-version)
(define get
(foreign-procedure "dm_version" ((* DMIoctlInterface)
(* unsigned-32)
(* unsigned-32)
(* unsigned-32)) int))
(with-u32s (major minor patch)
(if (zero? (get (current-dm-interface) major minor patch))
(let ((r (make-dm-version (deref-u32 major)
(deref-u32 minor)
(deref-u32 patch))))
r)
(fail "couldn't get dm version"))))
(define (remove-all)
(define do-it
(foreign-procedure "dm_remove_all" ((* DMIoctlInterface)) int))
(let ((r (do-it (current-dm-interface))))
(unless (zero? r)
(fail "remove-all failed"))))
(define-ftype DevList
(struct
(next (* DevList))
(major unsigned)
(minor unsigned)
(name (* unsigned-8))))
(define-ftype DevListPtr (* DevList))
(define (cstring->string str)
(let loop ((i 0)
(acc '()))
(let ((c (ftype-ref unsigned-8 () str i)))
(if (zero? c)
(list->string (reverse acc))
(loop (+ i 1) (cons (integer->char c) acc))))))
(define (string->cstring str)
(let* ((len (string-length str))
(cstr (make-ftype-pointer unsigned-8
(foreign-alloc (+ 1 len)))))
;; FIXME: ugly; use for-each
(let loop ((i 0))
(if (= i len)
(begin
(ftype-set! unsigned-8 () cstr i 0)
cstr)
(begin
(ftype-set! unsigned-8 () cstr i (char->integer (string-ref str i)))
(loop (+ i 1)))))))
;;; FIXME: put a dynamic wind in to ensure the dev list gets freed
(define (list-devices)
(define list-devs
(foreign-procedure "dm_list_devices" ((* DMIoctlInterface) (* DevListPtr)) int))
(let ((pp (make-ftype-pointer DevListPtr
(foreign-alloc (ftype-sizeof DevListPtr)))))
(if (zero? (list-devs (current-dm-interface) pp))
(let loop ((dl (ftype-ref DevListPtr () pp))
(acc '()))
(if (ftype-pointer-null? dl)
acc
(loop (ftype-ref DevList (next) dl)
(cons (make-dm-device
(cstring->string (ftype-ref DevList (name) dl))
(ftype-ref DevList (major) dl)
(ftype-ref DevList (minor) dl))
acc))))
(fail "dm_list_devices ioctl failed"))))
(define (create-device name uuid)
(define create
(foreign-procedure "dm_create_device" ((* DMIoctlInterface) string string (* unsigned-32) (* unsigned-32)) int))
(with-u32s (major minor)
(let ((r (create (current-dm-interface) name uuid major minor)))
(if (zero? r)
(make-dm-device name (deref-u32 major) (deref-u32 minor))
(fail (fmt #f "create-device failed with error code " r))))))
(define-syntax define-dev-cmd
(syntax-rules ()
((_ nm proc)
(define (nm dev)
(define fn
(foreign-procedure proc ((* DMIoctlInterface) string) int))
(unless (zero? (fn (current-dm-interface) (dm-device-name dev)))
(fail (string-append proc " failed")))))))
(define-dev-cmd remove-device "dm_remove_device")
(define-dev-cmd suspend-device "dm_suspend_device")
(define-dev-cmd resume-device "dm_resume_device")
(define-dev-cmd clear-device "dm_clear_device")
(define-ftype Target
(struct
(next (* Target))
(len unsigned-64)
(type (* unsigned-8))
(args (* unsigned-8))))
(define-ftype TargetPtr (* Target))
(define-ftype TargetPtrPtr (* TargetPtr))
(define-record-type target
(fields (mutable len)
(mutable type)
(mutable args)))
(define (build-c-target next len type args)
(let ((t (make-ftype-pointer Target
(foreign-alloc
(ftype-sizeof Target)))))
(ftype-set! Target (next) t next)
(ftype-set! Target (len) t len)
(ftype-set! Target (type) t (string->cstring type))
(ftype-set! Target (args) t (string->cstring args))
t))
(define (build-c-targets targets)
(let loop ((targets (reverse targets))
(tail (make-ftype-pointer Target 0)))
(if (null? targets)
tail
(let ((t (car targets)))
(loop (cdr targets)
(build-c-target tail
(target-len t)
(target-type t)
(target-args t)))))))
#|
(define (free-c-target t)
(foreign-free (ftype-ref Target (type) t))
(foreign-free (ftype-ref Target (args) t))
(foreign-free t))
|#
(define (free-c-target t)
#f)
(define (free-c-targets t)
(let loop ((t t)
(acc '()))
(if (ftype-pointer-null? t)
(map free-c-target acc)
(loop (ftype-ref Target (next) t) (cons t acc)))))
(define-syntax ensure-free-ctargets
(syntax-rules ()
((_ ctargets b1 b2 ...)
(dynamic-wind
(lambda () #f)
(lambda () b1 b2 ...)
(lambda ()
(free-c-targets ctargets))))))
(define (load-table dev targets)
(define load
(foreign-procedure "dm_load" ((* DMIoctlInterface) string (* Target)) int))
(info dev " <- " targets)
(let* ((ctargets (build-c-targets targets)))
(ensure-free-ctargets ctargets
(unless (zero? (load (current-dm-interface) (dm-device-name dev) ctargets))
(fail "dm_load failed")))))
(define (with-empty-device-fn name uuid fn)
(let ((v (create-device name uuid)))
(dynamic-wind
(lambda () #f)
(lambda () (fn v))
(lambda () (remove-device v)))))
(define-syntax with-empty-device
(syntax-rules ()
((_ (var name uuid) b1 b2 ...)
(with-empty-device-fn name uuid (lambda (var) b1 b2 ...)))))
(define (with-device-fn name uuid table fn)
(with-empty-device-fn name uuid
(lambda (v)
(load-table v table)
(resume-device v)
(fn v))))
(define-syntax with-device
(syntax-rules ()
((_ (var name uuid table) b1 b2 ...)
(with-device-fn name uuid table (lambda (var) b1 b2 ...)))))
(define-syntax with-devices
(syntax-rules ()
((_ (dev) b1 b2 ...)
(with-device dev b1 b2 ...))
((_ (dev rest ...) b1 b2 ...)
(with-device dev
(with-devices (rest ...) b1 b2 ...)))))
(define (pause-device-thunk dev thunk)
(suspend-device dev)
(thunk)
(resume-device dev))
(define-syntax pause-device
(syntax-rules ()
((_ dev b1 b2 ...)
(pause-device-thunk dev (lambda () b1 b2 ...)))))
(define (do-status dev c-fn op-name)
(let ((tpp (make-ftype-pointer TargetPtr
(foreign-alloc (ftype-sizeof TargetPtrPtr)))))
(if (zero? (c-fn (current-dm-interface) (dm-device-name dev) tpp))
(let ((tp (ftype-ref TargetPtr () tpp)))
(ensure-free-ctargets tp
(let loop ((tp tp)
(acc '()))
(if (ftype-pointer-null? tp)
(reverse acc)
(loop (ftype-ref Target (next) tp)
(cons (make-target
(ftype-ref Target (len) tp)
(cstring->string (ftype-ref Target (type) tp))
(cstring->string (ftype-ref Target (args) tp)))
acc))))))
(fail (fmt #f op-name " ioctl failed")))))
(define (get-status dev)
(define get-status
(foreign-procedure "dm_status" ((* DMIoctlInterface) string (* TargetPtr)) int))
(do-status dev get-status "dm_status"))
(define (get-table dev)
(define get-status
(foreign-procedure "dm_table" ((* DMIoctlInterface) string (* TargetPtr)) int))
(do-status dev get-status "dm_table"))
(define (message dev sector msg)
(define c-message
(foreign-procedure "dm_message" ((* DMIoctlInterface) string unsigned-64 string) int))
(unless (zero? (c-message (current-dm-interface) (dm-device-name dev) sector msg))
(fail (fmt #f "message ioctl failed"))))
;; Works with either a raw path, or a dm-device. Returns a disk-size.
(define (get-dev-size dev)
(define c-get-size
(foreign-procedure "get_dev_size" (string (* unsigned-64)) int))
(let* ((path (if (string? dev) dev (dm-device-path dev)))
(size (make-ftype-pointer unsigned-64 (foreign-alloc (ftype-sizeof unsigned-64))))
(r (c-get-size path size)))
(let ((result (ftype-ref unsigned-64 () size)))
(foreign-free (ftype-pointer-address size))
(if (zero? r)
(sectors result)
(fail (fmt #f "get-dev-size failed: " r))))))
(define (discard dev sb se)
(define c-discard
(foreign-procedure "discard" (string unsigned-64 unsigned-64) int))
(unless (zero? (c-discard dev sb se))
(fail (fmt #f "discard ioctl failed"))))
)