[functional-tests] some thin/delete tests

This commit is contained in:
Joe Thornber 2017-12-14 14:58:16 +00:00
parent 93213135ad
commit 2db8ecf9e9
3 changed files with 260 additions and 48 deletions

View File

@ -1,4 +1,5 @@
#include <linux/dm-ioctl.h>
#include <linux/kdev_t.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
@ -239,7 +240,7 @@ static bool list_devices(struct dm_interface *dmi, struct dm_ioctl *ctl,
if (nl->dev) {
for (;;) {
dlb_append(&dlb, major(nl->dev), minor(nl->dev), nl->name);
dlb_append(&dlb, MAJOR(nl->dev), MINOR(nl->dev), nl->name);
if (!nl->next)
break;
@ -273,7 +274,9 @@ int dm_list_devices(struct dm_interface *dmi, struct dev_list **devs)
return r;
}
int dm_create_device(struct dm_interface *dmi, const char *name, const char *uuid)
// Obviously major and minor are only valid if successful.
int dm_create_device(struct dm_interface *dmi, const char *name, const char *uuid,
uint32_t *major_result, uint32_t *minor_result)
{
int r;
struct dm_ioctl *ctl = alloc_ctl(0);
@ -294,8 +297,11 @@ int dm_create_device(struct dm_interface *dmi, const char *name, const char *uui
}
r = ioctl(dmi->fd, DM_DEV_CREATE, ctl);
if (!r) {
*major_result = MAJOR(ctl->dev);
*minor_result = MINOR(ctl->dev);
}
free_ctl(ctl);
return r;
}

View File

@ -9,6 +9,7 @@
(fmt fmt)
(list-utils)
(loops)
(prefix (parser-combinators) p:)
(process)
(srfi s27 random-bits)
(temp-file)
@ -19,6 +20,7 @@
(define (register-dm-tests) #t)
;; FIXME: use memoisation to avoid running blockdev so much
;; FIXME: return a disk-size, and take a dm-device
(define (get-dev-size dev)
(run-ok-rcv (stdout stderr) (fmt #f "blockdev --getsz " dev)
(string->number (chomp stdout))))
@ -113,14 +115,21 @@
80 ;; low water mark
(length opts-str) opts-str)))))
;; FIXME: move somewhere else, and do IO in bigger blocks
(define (zero-dev dev size)
(define (dd . args)
(build-command-line (cons "dd" args)))
(define (dd-cmd . args)
(build-command-line (cons "dd" args)))
(run-ok (dd "if=/dev/zero"
(string-append "of=" (dm-device-path dev))
"bs=512" (fmt #f "count=" (to-sectors size)))))
;; FIXME: move somewhere else, and do IO in bigger blocks
(define zero-dev
(case-lambda
((dev)
(zero-dev dev
(sectors
(get-dev-size
(dm-device-path dev)))))
((dev size)
(run-ok (dd-cmd "if=/dev/zero"
(string-append "of=" (dm-device-path dev))
"bs=512" (fmt #f "count=" (to-sectors size)))))))
;; The contents should be
(define (with-ini-file-fn section contents fn)
@ -173,6 +182,14 @@
block-size
(lambda (pool) b1 b2 ...)))))
(define-syntax with-default-pool
(syntax-rules ()
((_ (pool) b1 b2 ...)
(with-pool (pool (default-md-table)
(default-data-table (gig 10))
(kilo 64))
b1 b2 ...))))
(define (default-md-table)
(list ((mk-fast-allocator) (meg 32))))
@ -198,6 +215,9 @@
(define (create-snap pool new-id origin-id)
(message pool 0 (fmt #f "create_snap " new-id " " origin-id)))
(define (delete-thin pool id)
(message pool 0 (fmt #f "delete " id)))
(define (with-thin-fn pool id size fn)
(with-device-fn (generate-dev-name) "" (thin-table pool id size) fn))
@ -216,6 +236,135 @@
(with-new-thin-fn pool id size (lambda (thin)
b1 b2 ...)))))
;;;-----------------------------------------------------------
;;; Pool status
;;;-----------------------------------------------------------
(define-record-type pool-status
(fields (mutable transaction-id)
(mutable used-metadata)
(mutable total-metadata)
(mutable used-data)
(mutable total-data)
(mutable held-root) ; (bool . root?)
(mutable needs-check) ; bool
(mutable discard) ; bool
(mutable discard-passdown) ; bool
(mutable block-zeroing) ; bool
(mutable io-mode) ; 'out-of-data-space, 'ro, 'rw
(mutable no-space-behaviour) ; 'error, 'queue
(mutable fail) ; bool
))
(define (default-pool-status)
(make-pool-status 0 ; trans id
0 ; used md
0 ; total md
0 ; used data
0 ; total data
(cons #f 0) ; held root
#f ; need check
#t ; discard
#t ; discard passdown
#t ; block zeroing
'rw ; io-mode
'queue ; no space behaviour
#f ; fail
))
(define digit (p:charset "0123456789"))
(define number
(p:lift (lambda (cs)
(string->number
(apply string cs)))
(p:many+ digit)))
(define held-root
(p:alt
(p:>> (p:lit "-")
(p:pure (cons #f 0)))
(p:parse-m (p:<- root number)
(p:pure (cons #t root)))))
(define space
(p:many+ (p:charset " \t")))
(define slash
(p:lit "/"))
;; The options parser returns a function that mutates the status.
(define-syntax opt-mut
(syntax-rules ()
((_ (status txt) b1 b2 ...)
(p:>> (p:lit txt)
(p:pure (lambda (status) b1 b2 ...))))))
(define pool-option
(p:one-of
(opt-mut (status "skip_block_zeroing")
(pool-status-block-zeroing-set! status #f))
(opt-mut (status "ignore_discard")
(pool-status-discard-set! status #f))
(opt-mut (status "no_discard_passdown")
(pool-status-discard-passdown-set! status #f))
(opt-mut (status "discard_passdown")
(pool-status-discard-passdown-set! status #t))
(opt-mut (status "out_of_data_space")
(pool-status-io-mode-set! status 'out-of-data-space))
(opt-mut (status "ro")
(pool-status-io-mode-set! status 'ro))
(opt-mut (status "rw")
(pool-status-io-mode-set! status 'rw))
(opt-mut (status "error_if_no_space")
(pool-status-no-space-behaviour-set! status 'error))
(opt-mut (status "queue_if_no_space")
(pool-status-no-space-behaviour-set! status 'queue))))
(define needs-check
(p:one-of
(p:>> (p:lit "needs_check")
(p:pure #t))
(p:pure #f)))
(define (parse-pool-status txt)
(p:parse-m (p:<- transaction-id number)
space
(p:<- used-metadata number)
slash
(p:<- total-metadata number)
space
(p:<- used-data number)
slash
(p:<- total-data number)
space
(p:<- metadata-snap held-root)
space
(p:<- options (p:many* (p:<* pool-option space)))
(p:<- check needs-check)
(let ((status (default-pool-status)))
(pool-status-transaction-id-set! status transaction-id)
(pool-status-used-metadata-set! status used-metadata)
(pool-status-total-metadata-set! status total-metadata)
(pool-status-used-data-set! status used-data)
(pool-status-total-data-set! status total-data)
(pool-status-held-root-set! status metadata-snap)
(pool-status-needs-check-set! status check)
(for-each (lambda (mut) (mut status)) options)
(p:pure status))))
(define (get-pool-status pool)
(p:parse-value parse-pool-status
(get-status pool)))
;;;-----------------------------------------------------------
;;; Fundamental dm scenarios
;;;-----------------------------------------------------------
@ -279,7 +428,7 @@
(let ((pv (mk-fast-allocator)))
(with-devices ((dev1 "foo" "uuid" (linear-table pv 4))
(dev2 "bar" "uuid2" (linear-table pv 4)))
(let ((names (map device-details-name (list-devices))))
(let ((names (map dm-device-name (list-devices))))
(assert-member? "foo" names)
(assert-member? "bar" names)))))
@ -385,17 +534,13 @@
(define-dm-scenario (thin create too-large-thin-dev-fails)
"The thin-id must be less 2^24"
(with-pool (pool (default-md-table)
(default-data-table (gig 10))
(kilo 64))
(with-default-pool (pool)
(assert-raises
(create-thin pool (expt 2 24)))))
(define-dm-scenario (thin create largest-thin-dev-succeeds)
"The thin-id must be less 2^24"
(with-pool (pool (default-md-table)
(default-data-table (gig 10))
(kilo 64))
(with-default-pool (pool)
(create-thin pool (- (expt 2 24) 1))))
(define-dm-scenario (thin create too-small-metadata-fails)
@ -405,5 +550,56 @@
(default-data-table (gig 10))
(kilo 64))
#t)))
;;;-----------------------------------------------------------
;;; Thin deletion scenarios
;;;-----------------------------------------------------------
(define-dm-scenario (thin delete create-delete-cycle)
"Create and delete a thin 1000 times"
(with-default-pool (pool)
(upto (n 1000)
(create-thin pool 0)
(delete-thin pool 0))))
(define-dm-scenario (thin delete create-delete-many)
"Create and delete 1000 thins"
(with-default-pool (pool)
(upto (n 1000)
(create-thin pool n))
(upto (n 1000)
(delete-thin pool n))))
(define-dm-scenario (thin delete rolling-create-delete)
"Create and delete 1000 thins"
(with-default-pool (pool)
(upto (n 1000)
(create-thin pool n))
(upto (n 1000)
(delete-thin pool n)
(create-thin pool n))))
(define-dm-scenario (thin delete unknown-id)
"Fails if the thin id is unknown"
(with-default-pool (pool)
(upto (n 100)
(create-thin pool (* n 100)))
(assert-raises
(delete-thin pool 57))))
(define-dm-scenario (thin delete active-device-fails)
"You can't delete an active device"
(with-default-pool (pool)
(with-new-thin (thin pool 0 (gig 1))
(assert-raises
(delete-thin pool 0)))))
#|
(define-dm-scenario (thin delete recover-space)
"Deleting a thin recovers data space"
(with-default-pool (pool)
(with-new-thin (thin pool 0 (gig 1))
;(zero-dev thin)
(fmt #t (get-pool-status pool)))))
|#
)

View File

@ -9,6 +9,8 @@
dm-device
dm-device-name
dm-device-path
dm-device-minor
dm-device-major
dm-version
get-version
@ -37,11 +39,6 @@
pause-device
pause-device-thunk
device-details
device-details-name
device-details-major
device-details-minor
get-status
get-table
@ -64,10 +61,10 @@
(struct
(fd int)))
(define-record-type dm-device (fields (mutable name)))
(define-record-type dm-device (fields name major minor))
(define (dm-device-path d)
(fmt #f (dsp "/dev/mapper/") (dsp (dm-device-name d))))
(fmt #f (dsp "/dev/dm-") (dsp (dm-device-minor d))))
(define open% (foreign-procedure "dm_open" () (* DMIoctlInterface)))
@ -100,6 +97,17 @@
(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)))
;; FIXME: make a with-u32s macro
(define (get-version)
(define get
(foreign-procedure "dm_version" ((* DMIoctlInterface)
@ -107,25 +115,22 @@
(* unsigned-32)
(* unsigned-32)) int))
(define (alloc-u32)
(make-ftype-pointer unsigned-32
(foreign-alloc (ftype-sizeof unsigned-32))))
(define (deref-u32 p)
(ftype-ref unsigned-32 () p))
(let ((major (alloc-u32))
(minor (alloc-u32))
(patch (alloc-u32)))
(if (zero? (get (current-dm-interface) major minor patch))
(let ((r (make-dm-version (deref-u32 major)
(deref-u32 minor)
(deref-u32 patch))))
(foreign-free (ftype-pointer-address major))
(foreign-free (ftype-pointer-address minor))
(foreign-free (ftype-pointer-address patch))
r)
(fail "couldn't get dm version"))))
(dynamic-wind
(lambda () #f)
(lambda ()
(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")))
(lambda ()
(free-u32 major)
(free-u32 minor)
(free-u32 patch)))))
(define (remove-all)
(define do-it
@ -144,9 +149,6 @@
(define-ftype DevListPtr (* DevList))
(define-record-type device-details
(fields name major minor))
(define (cstring->string str)
(let loop ((i 0)
(acc '()))
@ -182,7 +184,7 @@
(if (ftype-pointer-null? dl)
acc
(loop (ftype-ref DevList (next) dl)
(cons (make-device-details
(cons (make-dm-device
(cstring->string (ftype-ref DevList (name) dl))
(ftype-ref DevList (major) dl)
(ftype-ref DevList (minor) dl))
@ -191,11 +193,19 @@
(define (create-device name uuid)
(define create
(foreign-procedure "dm_create_device" ((* DMIoctlInterface) string string) int))
(foreign-procedure "dm_create_device" ((* DMIoctlInterface) string string (* unsigned-32) (* unsigned-32)) int))
(if (zero? (create (current-dm-interface) name uuid))
(make-dm-device name)
(fail "create-device failed")))
(let* ((major (alloc-u32))
(minor (alloc-u32)))
(dynamic-wind
(lambda () #f)
(lambda ()
(if (zero? (create (current-dm-interface) name uuid major minor))
(make-dm-device name (deref-u32 major) (deref-u32 minor))
(fail "create-device failed")))
(lambda ()
(free-u32 major)
(free-u32 minor)))))
(define-syntax define-dev-cmd
(syntax-rules ()