Repository: prometheus/procfs
Pull request #190 (2019-07-12 10:05:24)

Add VulnerabilityStats() to for kernel vulnerabilities
Replaced #106


Patch:
diff --git a/arp.go b/arp.go
new file mode 100644
index 0000000..916c918
--- /dev/null
+++ b/arp.go
@@ -0,0 +1,85 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+	"fmt"
+	"io/ioutil"
+	"net"
+	"strings"
+)
+
+// ARPEntry contains a single row of the columnar data represented in
+// /proc/net/arp.
+type ARPEntry struct {
+	// IP address
+	IPAddr net.IP
+	// MAC address
+	HWAddr net.HardwareAddr
+	// Name of the device
+	Device string
+}
+
+// GatherARPEntries retrieves all the ARP entries, parse the relevant columns,
+// and then return a slice of ARPEntry's.
+func (fs FS) GatherARPEntries() ([]ARPEntry, error) {
+	data, err := ioutil.ReadFile(fs.proc.Path("net/arp"))
+	if err != nil {
+		return nil, fmt.Errorf("error reading arp %s: %s", fs.proc.Path("net/arp"), err)
+	}
+
+	return parseARPEntries(data)
+}
+
+func parseARPEntries(data []byte) ([]ARPEntry, error) {
+	lines := strings.Split(string(data), "\n")
+	entries := make([]ARPEntry, 0)
+	var err error
+	const (
+		expectedDataWidth   = 6
+		expectedHeaderWidth = 9
+	)
+	for _, line := range lines {
+		columns := strings.Fields(line)
+		width := len(columns)
+
+		if width == expectedHeaderWidth || width == 0 {
+			continue
+		} else if width == expectedDataWidth {
+			entry, err := parseARPEntry(columns)
+			if err != nil {
+				return []ARPEntry{}, fmt.Errorf("failed to parse ARP entry: %s", err)
+			}
+			entries = append(entries, entry)
+		} else {
+			return []ARPEntry{}, fmt.Errorf("%d columns were detected, but %d were expected", width, expectedDataWidth)
+		}
+
+	}
+
+	return entries, err
+}
+
+func parseARPEntry(columns []string) (ARPEntry, error) {
+	ip := net.ParseIP(columns[0])
+	mac := net.HardwareAddr(columns[3])
+
+	entry := ARPEntry{
+		IPAddr: ip,
+		HWAddr: mac,
+		Device: columns[5],
+	}
+
+	return entry, nil
+}
diff --git a/crypto.go b/crypto.go
new file mode 100644
index 0000000..19d4041
--- /dev/null
+++ b/crypto.go
@@ -0,0 +1,131 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"strconv"
+	"strings"
+
+	"github.com/prometheus/procfs/internal/util"
+)
+
+// Crypto holds info parsed from /proc/crypto.
+type Crypto struct {
+	Alignmask   *uint64
+	Async       bool
+	Blocksize   *uint64
+	Chunksize   *uint64
+	Ctxsize     *uint64
+	Digestsize  *uint64
+	Driver      string
+	Geniv       string
+	Internal    string
+	Ivsize      *uint64
+	Maxauthsize *uint64
+	MaxKeysize  *uint64
+	MinKeysize  *uint64
+	Module      string
+	Name        string
+	Priority    *int64
+	Refcnt      *int64
+	Seedsize    *uint64
+	Selftest    string
+	Type        string
+	Walksize    *uint64
+}
+
+// Crypto parses an crypto-file (/proc/crypto) and returns a slice of
+// structs containing the relevant info.  More information available here:
+// https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html
+func (fs FS) Crypto() ([]Crypto, error) {
+	data, err := ioutil.ReadFile(fs.proc.Path("crypto"))
+	if err != nil {
+		return nil, fmt.Errorf("error parsing crypto %s: %s", fs.proc.Path("crypto"), err)
+	}
+	crypto, err := parseCrypto(data)
+	if err != nil {
+		return nil, fmt.Errorf("error parsing crypto %s: %s", fs.proc.Path("crypto"), err)
+	}
+	return crypto, nil
+}
+
+func parseCrypto(cryptoData []byte) ([]Crypto, error) {
+	crypto := []Crypto{}
+
+	cryptoBlocks := bytes.Split(cryptoData, []byte("\n\n"))
+
+	for _, block := range cryptoBlocks {
+		var newCryptoElem Crypto
+
+		lines := strings.Split(string(block), "\n")
+		for _, line := range lines {
+			if strings.TrimSpace(line) == "" || line[0] == ' ' {
+				continue
+			}
+			fields := strings.Split(line, ":")
+			key := strings.TrimSpace(fields[0])
+			value := strings.TrimSpace(fields[1])
+			vp := util.NewValueParser(value)
+
+			switch strings.TrimSpace(key) {
+			case "async":
+				b, err := strconv.ParseBool(value)
+				if err == nil {
+					newCryptoElem.Async = b
+				}
+			case "blocksize":
+				newCryptoElem.Blocksize = vp.PUInt64()
+			case "chunksize":
+				newCryptoElem.Chunksize = vp.PUInt64()
+			case "digestsize":
+				newCryptoElem.Digestsize = vp.PUInt64()
+			case "driver":
+				newCryptoElem.Driver = value
+			case "geniv":
+				newCryptoElem.Geniv = value
+			case "internal":
+				newCryptoElem.Internal = value
+			case "ivsize":
+				newCryptoElem.Ivsize = vp.PUInt64()
+			case "maxauthsize":
+				newCryptoElem.Maxauthsize = vp.PUInt64()
+			case "max keysize":
+				newCryptoElem.MaxKeysize = vp.PUInt64()
+			case "min keysize":
+				newCryptoElem.MinKeysize = vp.PUInt64()
+			case "module":
+				newCryptoElem.Module = value
+			case "name":
+				newCryptoElem.Name = value
+			case "priority":
+				newCryptoElem.Priority = vp.PInt64()
+			case "refcnt":
+				newCryptoElem.Refcnt = vp.PInt64()
+			case "seedsize":
+				newCryptoElem.Seedsize = vp.PUInt64()
+			case "selftest":
+				newCryptoElem.Selftest = value
+			case "type":
+				newCryptoElem.Type = value
+			case "walksize":
+				newCryptoElem.Walksize = vp.PUInt64()
+			}
+		}
+		crypto = append(crypto, newCryptoElem)
+	}
+	return crypto, nil
+}
diff --git a/fixtures.ttar b/fixtures.ttar
index b743744..8591c27 100644
--- a/fixtures.ttar
+++ b/fixtures.ttar
@@ -333,6 +333,981 @@ Node 0, zone    DMA32    759    572    791    475    194     45     12      0
 Node 0, zone   Normal   4381   1093    185   1530    567    102      4      0      0      0      0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/crypto
+Lines: 971
+name         : ccm(aes)
+driver       : ccm_base(ctr(aes-aesni),cbcmac(aes-aesni))
+module       : ccm
+priority     : 300
+refcnt       : 4
+selftest     : passed
+internal     : no
+type         : aead
+async        : no
+blocksize    : 1
+ivsize       : 16
+maxauthsize  : 16
+geniv        : <none>
+
+name         : cbcmac(aes)
+driver       : cbcmac(aes-aesni)
+module       : ccm
+priority     : 300
+refcnt       : 7
+selftest     : passed
+internal     : no
+type         : shash
+blocksize    : 1
+digestsize   : 16
+
+name         : ecdh
+driver       : ecdh-generic
+module       : ecdh_generic
+priority     : 100
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : kpp
+
+name         : ecb(arc4)
+driver       : ecb(arc4)-generic
+module       : arc4
+priority     : 100
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : skcipher
+async        : no
+blocksize    : 1
+min keysize  : 1
+max keysize  : 256
+ivsize       : 0
+chunksize    : 1
+walksize     : 1
+
+name         : arc4
+driver       : arc4-generic
+module       : arc4
+priority     : 0
+refcnt       : 3
+selftest     : passed
+internal     : no
+type         : cipher
+blocksize    : 1
+min keysize  : 1
+max keysize  : 256
+
+name         : crct10dif
+driver       : crct10dif-pclmul
+module       : crct10dif_pclmul
+priority     : 200
+refcnt       : 2
+selftest     : passed
+internal     : no
+type         : shash
+blocksize    : 1
+digestsize   : 2
+
+name         : crc32
+driver       : crc32-pclmul
+module       : crc32_pclmul
+priority     : 200
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : shash
+blocksize    : 1
+digestsize   : 4
+
+name         : __ghash
+driver       : cryptd(__ghash-pclmulqdqni)
+module       : kernel
+priority     : 50
+refcnt       : 1
+selftest     : passed
+internal     : yes
+type         : ahash
+async        : yes
+blocksize    : 16
+digestsize   : 16
+
+name         : ghash
+driver       : ghash-clmulni
+module       : ghash_clmulni_intel
+priority     : 400
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : ahash
+async        : yes
+blocksize    : 16
+digestsize   : 16
+
+name         : __ghash
+driver       : __ghash-pclmulqdqni
+module       : ghash_clmulni_intel
+priority     : 0
+refcnt       : 1
+selftest     : passed
+internal     : yes
+type         : shash
+blocksize    : 16
+digestsize   : 16
+
+name         : crc32c
+driver       : crc32c-intel
+module       : crc32c_intel
+priority     : 200
+refcnt       : 5
+selftest     : passed
+internal     : no
+type         : shash
+blocksize    : 1
+digestsize   : 4
+
+name         : cbc(aes)
+driver       : cbc(aes-aesni)
+module       : kernel
+priority     : 300
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : skcipher
+async        : no
+blocksize    : 16
+min keysize  : 16
+max keysize  : 32
+ivsize       : 16
+chunksize    : 16
+walksize     : 16
+
+name         : ctr(aes)
+driver       : ctr(aes-aesni)
+module       : kernel
+priority     : 300
+refcnt       : 5
+selftest     : passed
+internal     : no
+type         : skcipher
+async        : no
+blocksize    : 1
+min keysize  : 16
+max keysize  : 32
+ivsize       : 16
+chunksize    : 16
+walksize     : 16
+
+name         : pkcs1pad(rsa,sha256)
+driver       : pkcs1pad(rsa-generic,sha256)
+module       : kernel
+priority     : 100
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : akcipher
+
+name         : __xts(aes)
+driver       : cryptd(__xts-aes-aesni)
+module       : kernel
+priority     : 451
+refcnt       : 1
+selftest     : passed
+internal     : yes
+type         : skcipher
+async        : yes
+blocksize    : 16
+min keysize  : 32
+max keysize  : 64
+ivsize       : 16
+chunksize    : 16
+walksize     : 16
+
+name         : xts(aes)
+driver       : xts-aes-aesni
+module       : kernel
+priority     : 401
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : skcipher
+async        : yes
+blocksize    : 16
+min keysize  : 32
+max keysize  : 64
+ivsize       : 16
+chunksize    : 16
+walksize     : 16
+
+name         : __ctr(aes)
+driver       : cryptd(__ctr-aes-aesni)
+module       : kernel
+priority     : 450
+refcnt       : 1
+selftest     : passed
+internal     : yes
+type         : skcipher
+async        : yes
+blocksize    : 1
+min keysize  : 16
+max keysize  : 32
+ivsize       : 16
+chunksize    : 16
+walksize     : 16
+
+name         : ctr(aes)
+driver       : ctr-aes-aesni
+module       : kernel
+priority     : 400
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : skcipher
+async        : yes
+blocksize    : 1
+min keysize  : 16
+max keysize  : 32
+ivsize       : 16
+chunksize    : 16
+walksize     : 16
+
+name         : __cbc(aes)
+driver       : cryptd(__cbc-aes-aesni)
+module       : kernel
+priority     : 450
+refcnt       : 1
+selftest     : passed
+internal     : yes
+type         : skcipher
+async        : yes
+blocksize    : 16
+min keysize  : 16
+max keysize  : 32
+ivsize       : 16
+chunksize    : 16
+walksize     : 16
+
+name         : cbc(aes)
+driver       : cbc-aes-aesni
+module       : kernel
+priority     : 400
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : skcipher
+async        : yes
+blocksize    : 16
+min keysize  : 16
+max keysize  : 32
+ivsize       : 16
+chunksize    : 16
+walksize     : 16
+
+name         : __ecb(aes)
+driver       : cryptd(__ecb-aes-aesni)
+module       : kernel
+priority     : 450
+refcnt       : 1
+selftest     : passed
+internal     : yes
+type         : skcipher
+async        : yes
+blocksize    : 16
+min keysize  : 16
+max keysize  : 32
+ivsize       : 0
+chunksize    : 16
+walksize     : 16
+
+name         : ecb(aes)
+driver       : ecb-aes-aesni
+module       : kernel
+priority     : 400
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : skcipher
+async        : yes
+blocksize    : 16
+min keysize  : 16
+max keysize  : 32
+ivsize       : 0
+chunksize    : 16
+walksize     : 16
+
+name         : __generic-gcm-aes-aesni
+driver       : cryptd(__driver-generic-gcm-aes-aesni)
+module       : kernel
+priority     : 50
+refcnt       : 1
+selftest     : passed
+internal     : yes
+type         : aead
+async        : yes
+blocksize    : 1
+ivsize       : 12
+maxauthsize  : 16
+geniv        : <none>
+
+name         : gcm(aes)
+driver       : generic-gcm-aesni
+module       : kernel
+priority     : 400
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : aead
+async        : yes
+blocksize    : 1
+ivsize       : 12
+maxauthsize  : 16
+geniv        : <none>
+
+name         : __generic-gcm-aes-aesni
+driver       : __driver-generic-gcm-aes-aesni
+module       : kernel
+priority     : 0
+refcnt       : 1
+selftest     : passed
+internal     : yes
+type         : aead
+async        : no
+blocksize    : 1
+ivsize       : 12
+maxauthsize  : 16
+geniv        : <none>
+
+name         : __gcm-aes-aesni
+driver       : cryptd(__driver-gcm-aes-aesni)
+module       : kernel
+priority     : 50
+refcnt       : 1
+selftest     : passed
+internal     : yes
+type         : aead
+async        : yes
+blocksize    : 1
+ivsize       : 8
+maxauthsize  : 16
+geniv        : <none>
+
+name         : rfc4106(gcm(aes))
+driver       : rfc4106-gcm-aesni
+module       : kernel
+priority     : 400
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : aead
+async        : yes
+blocksize    : 1
+ivsize       : 8
+maxauthsize  : 16
+geniv        : <none>
+
+name         : __gcm-aes-aesni
+driver       : __driver-gcm-aes-aesni
+module       : kernel
+priority     : 0
+refcnt       : 1
+selftest     : passed
+internal     : yes
+type         : aead
+async        : no
+blocksize    : 1
+ivsize       : 8
+maxauthsize  : 16
+geniv        : <none>
+
+name         : __xts(aes)
+driver       : __xts-aes-aesni
+module       : kernel
+priority     : 401
+refcnt       : 1
+selftest     : passed
+internal     : yes
+type         : skcipher
+async        : no
+blocksize    : 16
+min keysize  : 32
+max keysize  : 64
+ivsize       : 16
+chunksize    : 16
+walksize     : 16
+
+name         : __ctr(aes)
+driver       : __ctr-aes-aesni
+module       : kernel
+priority     : 400
+refcnt       : 1
+selftest     : passed
+internal     : yes
+type         : skcipher
+async        : no
+blocksize    : 1
+min keysize  : 16
+max keysize  : 32
+ivsize       : 16
+chunksize    : 16
+walksize     : 16
+
+name         : __cbc(aes)
+driver       : __cbc-aes-aesni
+module       : kernel
+priority     : 400
+refcnt       : 1
+selftest     : passed
+internal     : yes
+type         : skcipher
+async        : no
+blocksize    : 16
+min keysize  : 16
+max keysize  : 32
+ivsize       : 16
+chunksize    : 16
+walksize     : 16
+
+name         : __ecb(aes)
+driver       : __ecb-aes-aesni
+module       : kernel
+priority     : 400
+refcnt       : 1
+selftest     : passed
+internal     : yes
+type         : skcipher
+async        : no
+blocksize    : 16
+min keysize  : 16
+max keysize  : 32
+ivsize       : 0
+chunksize    : 16
+walksize     : 16
+
+name         : __aes
+driver       : __aes-aesni
+module       : kernel
+priority     : 300
+refcnt       : 1
+selftest     : passed
+internal     : yes
+type         : cipher
+blocksize    : 16
+min keysize  : 16
+max keysize  : 32
+
+name         : aes
+driver       : aes-aesni
+module       : kernel
+priority     : 300
+refcnt       : 8
+selftest     : passed
+internal     : no
+type         : cipher
+blocksize    : 16
+min keysize  : 16
+max keysize  : 32
+
+name         : hmac(sha1)
+driver       : hmac(sha1-generic)
+module       : kernel
+priority     : 100
+refcnt       : 9
+selftest     : passed
+internal     : no
+type         : shash
+blocksize    : 64
+digestsize   : 20
+
+name         : ghash
+driver       : ghash-generic
+module       : kernel
+priority     : 100
+refcnt       : 3
+selftest     : passed
+internal     : no
+type         : shash
+blocksize    : 16
+digestsize   : 16
+
+name         : jitterentropy_rng
+driver       : jitterentropy_rng
+module       : kernel
+priority     : 100
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_nopr_hmac_sha256
+module       : kernel
+priority     : 221
+refcnt       : 2
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_nopr_hmac_sha512
+module       : kernel
+priority     : 220
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_nopr_hmac_sha384
+module       : kernel
+priority     : 219
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_nopr_hmac_sha1
+module       : kernel
+priority     : 218
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_nopr_sha256
+module       : kernel
+priority     : 217
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_nopr_sha512
+module       : kernel
+priority     : 216
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_nopr_sha384
+module       : kernel
+priority     : 215
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_nopr_sha1
+module       : kernel
+priority     : 214
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_nopr_ctr_aes256
+module       : kernel
+priority     : 213
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_nopr_ctr_aes192
+module       : kernel
+priority     : 212
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_nopr_ctr_aes128
+module       : kernel
+priority     : 211
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : hmac(sha256)
+driver       : hmac(sha256-generic)
+module       : kernel
+priority     : 100
+refcnt       : 10
+selftest     : passed
+internal     : no
+type         : shash
+blocksize    : 64
+digestsize   : 32
+
+name         : stdrng
+driver       : drbg_pr_hmac_sha256
+module       : kernel
+priority     : 210
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_pr_hmac_sha512
+module       : kernel
+priority     : 209
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_pr_hmac_sha384
+module       : kernel
+priority     : 208
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_pr_hmac_sha1
+module       : kernel
+priority     : 207
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_pr_sha256
+module       : kernel
+priority     : 206
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_pr_sha512
+module       : kernel
+priority     : 205
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_pr_sha384
+module       : kernel
+priority     : 204
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_pr_sha1
+module       : kernel
+priority     : 203
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_pr_ctr_aes256
+module       : kernel
+priority     : 202
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_pr_ctr_aes192
+module       : kernel
+priority     : 201
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : stdrng
+driver       : drbg_pr_ctr_aes128
+module       : kernel
+priority     : 200
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : rng
+seedsize     : 0
+
+name         : 842
+driver       : 842-scomp
+module       : kernel
+priority     : 100
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : scomp
+
+name         : 842
+driver       : 842-generic
+module       : kernel
+priority     : 100
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : compression
+
+name         : lzo-rle
+driver       : lzo-rle-scomp
+module       : kernel
+priority     : 0
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : scomp
+
+name         : lzo-rle
+driver       : lzo-rle-generic
+module       : kernel
+priority     : 0
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : compression
+
+name         : lzo
+driver       : lzo-scomp
+module       : kernel
+priority     : 0
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : scomp
+
+name         : lzo
+driver       : lzo-generic
+module       : kernel
+priority     : 0
+refcnt       : 9
+selftest     : passed
+internal     : no
+type         : compression
+
+name         : crct10dif
+driver       : crct10dif-generic
+module       : kernel
+priority     : 100
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : shash
+blocksize    : 1
+digestsize   : 2
+
+name         : crc32c
+driver       : crc32c-generic
+module       : kernel
+priority     : 100
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : shash
+blocksize    : 1
+digestsize   : 4
+
+name         : zlib-deflate
+driver       : zlib-deflate-scomp
+module       : kernel
+priority     : 0
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : scomp
+
+name         : deflate
+driver       : deflate-scomp
+module       : kernel
+priority     : 0
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : scomp
+
+name         : deflate
+driver       : deflate-generic
+module       : kernel
+priority     : 0
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : compression
+
+name         : aes
+driver       : aes-generic
+module       : kernel
+priority     : 100
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : cipher
+blocksize    : 16
+min keysize  : 16
+max keysize  : 32
+
+name         : sha224
+driver       : sha224-generic
+module       : kernel
+priority     : 100
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : shash
+blocksize    : 64
+digestsize   : 28
+
+name         : sha256
+driver       : sha256-generic
+module       : kernel
+priority     : 100
+refcnt       : 11
+selftest     : passed
+internal     : no
+type         : shash
+blocksize    : 64
+digestsize   : 32
+
+name         : sha1
+driver       : sha1-generic
+module       : kernel
+priority     : 100
+refcnt       : 11
+selftest     : passed
+internal     : no
+type         : shash
+blocksize    : 64
+digestsize   : 20
+
+name         : md5
+driver       : md5-generic
+module       : kernel
+priority     : 0
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : shash
+blocksize    : 64
+digestsize   : 16
+
+name         : ecb(cipher_null)
+driver       : ecb-cipher_null
+module       : kernel
+priority     : 100
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : skcipher
+async        : no
+blocksize    : 1
+min keysize  : 0
+max keysize  : 0
+ivsize       : 0
+chunksize    : 1
+walksize     : 1
+
+name         : digest_null
+driver       : digest_null-generic
+module       : kernel
+priority     : 0
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : shash
+blocksize    : 1
+digestsize   : 0
+
+name         : compress_null
+driver       : compress_null-generic
+module       : kernel
+priority     : 0
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : compression
+
+name         : cipher_null
+driver       : cipher_null-generic
+module       : kernel
+priority     : 0
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : cipher
+blocksize    : 1
+min keysize  : 0
+max keysize  : 0
+
+name         : rsa
+driver       : rsa-generic
+module       : kernel
+priority     : 100
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : akcipher
+
+name         : dh
+driver       : dh-generic
+module       : kernel
+priority     : 100
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : kpp
+
+name         : aes
+driver       : aes-asm
+module       : kernel
+priority     : 200
+refcnt       : 1
+selftest     : passed
+internal     : no
+type         : cipher
+blocksize    : 16
+min keysize  : 16
+max keysize  : 32
+
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Path: fixtures/proc/diskstats
 Lines: 49
    1       0 ram0 0 0 0 0 0 0 0 0 0 0 0
@@ -482,6 +1457,12 @@ Mode: 644
 Directory: fixtures/proc/net
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/net/arp
+Lines: 2
+IP address       HW type     Flags       HW address            Mask     Device
+192.168.224.1    0x1         0x2         00:50:56:c0:00:08     *        ens33
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Path: fixtures/proc/net/dev
 Lines: 6
 Inter-|   Receive                                                |  Transmit
@@ -554,6 +1535,11 @@ proc4 2 2 10853
 proc4ops 72 0 0 0 1098 2 0 0 0 0 8179 5896 0 0 0 0 5900 0 0 2 0 2 0 9609 0 2 150 1272 0 0 0 1236 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/net/softnet_stat
+Lines: 1
+00015c73 00020e76 F0000769 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Path: fixtures/proc/net/unix
 Lines: 6
 Num       RefCount Protocol Flags    Type St Inode Path
@@ -909,6 +1895,272 @@ Lines: 1
 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/zoneinfo
+Lines: 262
+Node 0, zone      DMA
+  per-node stats
+      nr_inactive_anon 230981
+      nr_active_anon 547580
+      nr_inactive_file 316904
+      nr_active_file 346282
+      nr_unevictable 115467
+      nr_slab_reclaimable 131220
+      nr_slab_unreclaimable 47320
+      nr_isolated_anon 0
+      nr_isolated_file 0
+      workingset_nodes 11627
+      workingset_refault 466886
+      workingset_activate 276925
+      workingset_restore 84055
+      workingset_nodereclaim 487
+      nr_anon_pages 795576
+      nr_mapped    215483
+      nr_file_pages 761874
+      nr_dirty     908
+      nr_writeback 0
+      nr_writeback_temp 0
+      nr_shmem     224925
+      nr_shmem_hugepages 0
+      nr_shmem_pmdmapped 0
+      nr_anon_transparent_hugepages 0
+      nr_unstable  0
+      nr_vmscan_write 12950
+      nr_vmscan_immediate_reclaim 3033
+      nr_dirtied   8007423
+      nr_written   7752121
+      nr_kernel_misc_reclaimable 0
+  pages free     3952
+        min      33
+        low      41
+        high     49
+        spanned  4095
+        present  3975
+        managed  3956
+        protection: (0, 2877, 7826, 7826, 7826)
+      nr_free_pages 3952
+      nr_zone_inactive_anon 0
+      nr_zone_active_anon 0
+      nr_zone_inactive_file 0
+      nr_zone_active_file 0
+      nr_zone_unevictable 0
+      nr_zone_write_pending 0
+      nr_mlock     0
+      nr_page_table_pages 0
+      nr_kernel_stack 0
+      nr_bounce    0
+      nr_zspages   0
+      nr_free_cma  0
+      numa_hit     1
+      numa_miss    0
+      numa_foreign 0
+      numa_interleave 0
+      numa_local   1
+      numa_other   0
+  pagesets
+    cpu: 0
+              count: 0
+              high:  0
+              batch: 1
+  vm stats threshold: 8
+    cpu: 1
+              count: 0
+              high:  0
+              batch: 1
+  vm stats threshold: 8
+    cpu: 2
+              count: 0
+              high:  0
+              batch: 1
+  vm stats threshold: 8
+    cpu: 3
+              count: 0
+              high:  0
+              batch: 1
+  vm stats threshold: 8
+    cpu: 4
+              count: 0
+              high:  0
+              batch: 1
+  vm stats threshold: 8
+    cpu: 5
+              count: 0
+              high:  0
+              batch: 1
+  vm stats threshold: 8
+    cpu: 6
+              count: 0
+              high:  0
+              batch: 1
+  vm stats threshold: 8
+    cpu: 7
+              count: 0
+              high:  0
+              batch: 1
+  vm stats threshold: 8
+  node_unreclaimable:  0
+  start_pfn:           1
+Node 0, zone    DMA32
+  pages free     204252
+        min      19510
+        low      21059
+        high     22608
+        spanned  1044480
+        present  759231
+        managed  742806
+        protection: (0, 0, 4949, 4949, 4949)
+      nr_free_pages 204252
+      nr_zone_inactive_anon 118558
+      nr_zone_active_anon 106598
+      nr_zone_inactive_file 75475
+      nr_zone_active_file 70293
+      nr_zone_unevictable 66195
+      nr_zone_write_pending 64
+      nr_mlock     4
+      nr_page_table_pages 1756
+      nr_kernel_stack 2208
+      nr_bounce    0
+      nr_zspages   0
+      nr_free_cma  0
+      numa_hit     113952967
+      numa_miss    0
+      numa_foreign 0
+      numa_interleave 0
+      numa_local   113952967
+      numa_other   0
+  pagesets
+    cpu: 0
+              count: 345
+              high:  378
+              batch: 63
+  vm stats threshold: 48
+    cpu: 1
+              count: 356
+              high:  378
+              batch: 63
+  vm stats threshold: 48
+    cpu: 2
+              count: 325
+              high:  378
+              batch: 63
+  vm stats threshold: 48
+    cpu: 3
+              count: 346
+              high:  378
+              batch: 63
+  vm stats threshold: 48
+    cpu: 4
+              count: 321
+              high:  378
+              batch: 63
+  vm stats threshold: 48
+    cpu: 5
+              count: 316
+              high:  378
+              batch: 63
+  vm stats threshold: 48
+    cpu: 6
+              count: 373
+              high:  378
+              batch: 63
+  vm stats threshold: 48
+    cpu: 7
+              count: 339
+              high:  378
+              batch: 63
+  vm stats threshold: 48
+  node_unreclaimable:  0
+  start_pfn:           4096
+Node 0, zone   Normal
+  pages free     18553
+        min      11176
+        low      13842
+        high     16508
+        spanned  1308160
+        present  1308160
+        managed  1268711
+        protection: (0, 0, 0, 0, 0)
+      nr_free_pages 18553
+      nr_zone_inactive_anon 112423
+      nr_zone_active_anon 440982
+      nr_zone_inactive_file 241429
+      nr_zone_active_file 275989
+      nr_zone_unevictable 49272
+      nr_zone_write_pending 844
+      nr_mlock     154
+      nr_page_table_pages 9750
+      nr_kernel_stack 15136
+      nr_bounce    0
+      nr_zspages   0
+      nr_free_cma  0
+      numa_hit     162718019
+      numa_miss    0
+      numa_foreign 0
+      numa_interleave 26812
+      numa_local   162718019
+      numa_other   0
+  pagesets
+    cpu: 0
+              count: 316
+              high:  378
+              batch: 63
+  vm stats threshold: 56
+    cpu: 1
+              count: 366
+              high:  378
+              batch: 63
+  vm stats threshold: 56
+    cpu: 2
+              count: 60
+              high:  378
+              batch: 63
+  vm stats threshold: 56
+    cpu: 3
+              count: 256
+              high:  378
+              batch: 63
+  vm stats threshold: 56
+    cpu: 4
+              count: 253
+              high:  378
+              batch: 63
+  vm stats threshold: 56
+    cpu: 5
+              count: 159
+              high:  378
+              batch: 63
+  vm stats threshold: 56
+    cpu: 6
+              count: 311
+              high:  378
+              batch: 63
+  vm stats threshold: 56
+    cpu: 7
+              count: 264
+              high:  378
+              batch: 63
+  vm stats threshold: 56
+  node_unreclaimable:  0
+  start_pfn:           1048576
+Node 0, zone  Movable
+  pages free     0
+        min      0
+        low      0
+        high     0
+        spanned  0
+        present  0
+        managed  0
+        protection: (0, 0, 0, 0, 0)
+Node 0, zone   Device
+  pages free     0
+        min      0
+        low      0
+        high     0
+        spanned  0
+        present  0
+        managed  0
+        protection: (0, 0, 0, 0, 0)
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Directory: fixtures/sys
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -1435,6 +2687,42 @@ Mode: 444
 Directory: fixtures/sys/class/thermal
 Mode: 775
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/class/thermal/cooling_device0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/thermal/cooling_device0/cur_state
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/thermal/cooling_device0/max_state
+Lines: 1
+50
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/thermal/cooling_device0/type
+Lines: 1
+Processor
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/class/thermal/cooling_device1
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/thermal/cooling_device1/cur_state
+Lines: 1
+-1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/thermal/cooling_device1/max_state
+Lines: 1
+27
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/thermal/cooling_device1/type
+Lines: 1
+intel_powerclamp
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Directory: fixtures/sys/class/thermal/thermal_zone0
 Mode: 775
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/internal/util/parse.go b/internal/util/parse.go
index ca74889..755591d 100644
--- a/internal/util/parse.go
+++ b/internal/util/parse.go
@@ -49,6 +49,21 @@ func ParseUint64s(ss []string) ([]uint64, error) {
 	return us, nil
 }
 
+// ParsePInt64s parses a slice of strings into a slice of int64 pointers.
+func ParsePInt64s(ss []string) ([]*int64, error) {
+	us := make([]*int64, 0, len(ss))
+	for _, s := range ss {
+		u, err := strconv.ParseInt(s, 10, 64)
+		if err != nil {
+			return nil, err
+		}
+
+		us = append(us, &u)
+	}
+
+	return us, nil
+}
+
 // ReadUintFromFile reads a file and attempts to parse a uint64 from it.
 func ReadUintFromFile(path string) (uint64, error) {
 	data, err := ioutil.ReadFile(path)
diff --git a/net_softnet.go b/net_softnet.go
new file mode 100644
index 0000000..6fcad20
--- /dev/null
+++ b/net_softnet.go
@@ -0,0 +1,91 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+	"fmt"
+	"io/ioutil"
+	"strconv"
+	"strings"
+)
+
+// For the proc file format details,
+// see https://elixir.bootlin.com/linux/v4.17/source/net/core/net-procfs.c#L162
+// and https://elixir.bootlin.com/linux/v4.17/source/include/linux/netdevice.h#L2810.
+
+// SoftnetEntry contains a single row of data from /proc/net/softnet_stat
+type SoftnetEntry struct {
+	// Number of processed packets
+	Processed uint
+	// Number of dropped packets
+	Dropped uint
+	// Number of times processing packets ran out of quota
+	TimeSqueezed uint
+}
+
+// GatherSoftnetStats reads /proc/net/softnet_stat, parse the relevant columns,
+// and then return a slice of SoftnetEntry's.
+func (fs FS) GatherSoftnetStats() ([]SoftnetEntry, error) {
+	data, err := ioutil.ReadFile(fs.proc.Path("net/softnet_stat"))
+	if err != nil {
+		return nil, fmt.Errorf("error reading softnet %s: %s", fs.proc.Path("net/softnet_stat"), err)
+	}
+
+	return parseSoftnetEntries(data)
+}
+
+func parseSoftnetEntries(data []byte) ([]SoftnetEntry, error) {
+	lines := strings.Split(string(data), "\n")
+	entries := make([]SoftnetEntry, 0)
+	var err error
+	const (
+		expectedColumns = 11
+	)
+	for _, line := range lines {
+		columns := strings.Fields(line)
+		width := len(columns)
+		if width == 0 {
+			continue
+		}
+		if width != expectedColumns {
+			return []SoftnetEntry{}, fmt.Errorf("%d columns were detected, but %d were expected", width, expectedColumns)
+		}
+		var entry SoftnetEntry
+		if entry, err = parseSoftnetEntry(columns); err != nil {
+			return []SoftnetEntry{}, err
+		}
+		entries = append(entries, entry)
+	}
+
+	return entries, nil
+}
+
+func parseSoftnetEntry(columns []string) (SoftnetEntry, error) {
+	var err error
+	var processed, dropped, timeSqueezed uint64
+	if processed, err = strconv.ParseUint(columns[0], 16, 32); err != nil {
+		return SoftnetEntry{}, fmt.Errorf("Unable to parse column 0: %s", err)
+	}
+	if dropped, err = strconv.ParseUint(columns[1], 16, 32); err != nil {
+		return SoftnetEntry{}, fmt.Errorf("Unable to parse column 1: %s", err)
+	}
+	if timeSqueezed, err = strconv.ParseUint(columns[2], 16, 32); err != nil {
+		return SoftnetEntry{}, fmt.Errorf("Unable to parse column 2: %s", err)
+	}
+	return SoftnetEntry{
+		Processed:    uint(processed),
+		Dropped:      uint(dropped),
+		TimeSqueezed: uint(timeSqueezed),
+	}, nil
+}
diff --git a/schedstat.go b/schedstat.go
index 86396c0..98221e3 100644
--- a/schedstat.go
+++ b/schedstat.go
@@ -31,6 +31,12 @@ var (
 // See
 // https://www.kernel.org/doc/Documentation/scheduler/sched-stats.txt
 // for a detailed description of what these numbers mean.
+//
+// Note the current kernel documentation claims some of the time units are in
+// jiffies when they are actually in nanoseconds since 2.6.23 with the
+// introduction of CFS. A fix to the documentation is pending. See
+// https://lore.kernel.org/patchwork/project/lkml/list/?series=403473
+
 type Schedstat struct {
 	CPUs []*SchedstatCPU
 }
@@ -39,16 +45,16 @@ type Schedstat struct {
 type SchedstatCPU struct {
 	CPUNum string
 
-	RunningJiffies uint64
-	WaitingJiffies uint64
-	RunTimeslices  uint64
+	RunningNanoseconds uint64
+	WaitingNanoseconds uint64
+	RunTimeslices      uint64
 }
 
 // ProcSchedstat contains the values from /proc/<pid>/schedstat
 type ProcSchedstat struct {
-	RunningJiffies uint64
-	WaitingJiffies uint64
-	RunTimeslices  uint64
+	RunningNanoseconds uint64
+	WaitingNanoseconds uint64
+	RunTimeslices      uint64
 }
 
 func (fs FS) Schedstat() (*Schedstat, error) {
@@ -67,12 +73,12 @@ func (fs FS) Schedstat() (*Schedstat, error) {
 			cpu := &SchedstatCPU{}
 			cpu.CPUNum = match[1]
 
-			cpu.RunningJiffies, err = strconv.ParseUint(match[8], 10, 64)
+			cpu.RunningNanoseconds, err = strconv.ParseUint(match[8], 10, 64)
 			if err != nil {
 				continue
 			}
 
-			cpu.WaitingJiffies, err = strconv.ParseUint(match[9], 10, 64)
+			cpu.WaitingNanoseconds, err = strconv.ParseUint(match[9], 10, 64)
 			if err != nil {
 				continue
 			}
@@ -93,12 +99,12 @@ func parseProcSchedstat(contents string) (stats ProcSchedstat, err error) {
 	match := procLineRE.FindStringSubmatch(contents)
 
 	if match != nil {
-		stats.RunningJiffies, err = strconv.ParseUint(match[1], 10, 64)
+		stats.RunningNanoseconds, err = strconv.ParseUint(match[1], 10, 64)
 		if err != nil {
 			return
 		}
 
-		stats.WaitingJiffies, err = strconv.ParseUint(match[2], 10, 64)
+		stats.WaitingNanoseconds, err = strconv.ParseUint(match[2], 10, 64)
 		if err != nil {
 			return
 		}
@@ -110,19 +116,3 @@ func parseProcSchedstat(contents string) (stats ProcSchedstat, err error) {
 	err = errors.New("could not parse schedstat")
 	return
 }
-
-func (stat *SchedstatCPU) RunningSeconds() float64 {
-	return float64(stat.RunningJiffies) / userHZ
-}
-
-func (stat *SchedstatCPU) WaitingSeconds() float64 {
-	return float64(stat.WaitingJiffies) / userHZ
-}
-
-func (stat *ProcSchedstat) RunningSeconds() float64 {
-	return float64(stat.RunningJiffies) / userHZ
-}
-
-func (stat *ProcSchedstat) WaitingSeconds() float64 {
-	return float64(stat.WaitingJiffies) / userHZ
-}
diff --git a/sysfs/class_cooling_device.go b/sysfs/class_cooling_device.go
new file mode 100644
index 0000000..8927b02
--- /dev/null
+++ b/sysfs/class_cooling_device.go
@@ -0,0 +1,90 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build !windows
+
+package sysfs
+
+import (
+	"path/filepath"
+	"strconv"
+	"strings"
+
+	"github.com/prometheus/procfs/internal/util"
+)
+
+// ClassCoolingDeviceStats contains info from files in /sys/class/thermal/cooling_device[0-9]*
+// for a single device.
+// https://www.kernel.org/doc/Documentation/thermal/sysfs-api.txt
+type ClassCoolingDeviceStats struct {
+	Name     string // The name of the cooling device.
+	Type     string // Type of the cooling device(processor/fan/...)
+	MaxState int64  // Maximum cooling state of the cooling device
+	CurState int64  // Current cooling state of the cooling device
+}
+
+func (fs FS) ClassCoolingDeviceStats() ([]ClassCoolingDeviceStats, error) {
+	cds, err := filepath.Glob(fs.sys.Path("class/thermal/cooling_device[0-9]*"))
+	if err != nil {
+		return []ClassCoolingDeviceStats{}, err
+	}
+
+	var coolingDeviceStats = ClassCoolingDeviceStats{}
+	stats := make([]ClassCoolingDeviceStats, len(cds))
+	for i, cd := range cds {
+		cdName := strings.TrimPrefix(filepath.Base(cd), "cooling_device")
+
+		coolingDeviceStats, err = parseCoolingDeviceStats(cd)
+		if err != nil {
+			return []ClassCoolingDeviceStats{}, err
+		}
+
+		coolingDeviceStats.Name = cdName
+		stats[i] = coolingDeviceStats
+	}
+	return stats, nil
+}
+
+func parseCoolingDeviceStats(cd string) (ClassCoolingDeviceStats, error) {
+	cdType, err := util.SysReadFile(filepath.Join(cd, "type"))
+	if err != nil {
+		return ClassCoolingDeviceStats{}, err
+	}
+
+	cdMaxStateString, err := util.SysReadFile(filepath.Join(cd, "max_state"))
+	if err != nil {
+		return ClassCoolingDeviceStats{}, err
+	}
+	cdMaxStateInt, err := strconv.ParseInt(cdMaxStateString, 10, 64)
+	if err != nil {
+		return ClassCoolingDeviceStats{}, err
+	}
+
+	// cur_state can be -1, eg intel powerclamp
+	// https://www.kernel.org/doc/Documentation/thermal/intel_powerclamp.txt
+	cdCurStateString, err := util.SysReadFile(filepath.Join(cd, "cur_state"))
+	if err != nil {
+		return ClassCoolingDeviceStats{}, err
+	}
+
+	cdCurStateInt, err := strconv.ParseInt(cdCurStateString, 10, 64)
+	if err != nil {
+		return ClassCoolingDeviceStats{}, err
+	}
+
+	return ClassCoolingDeviceStats{
+		Type:     cdType,
+		MaxState: cdMaxStateInt,
+		CurState: cdCurStateInt,
+	}, nil
+}
diff --git a/sysfs/net_class.go b/sysfs/net_class.go
index 909f7fb..d5c0d6a 100644
--- a/sysfs/net_class.go
+++ b/sysfs/net_class.go
@@ -18,11 +18,7 @@ package sysfs
 import (
 	"fmt"
 	"io/ioutil"
-	"os"
 	"path/filepath"
-	"reflect"
-	"strconv"
-	"strings"
 
 	"github.com/prometheus/procfs/internal/util"
 )
@@ -33,32 +29,32 @@ const netclassPath = "class/net"
 // for single interface (iface).
 type NetClassIface struct {
 	Name             string // Interface name
-	AddrAssignType   *int64 `fileName:"addr_assign_type"`   // /sys/class/net/<iface>/addr_assign_type
-	AddrLen          *int64 `fileName:"addr_len"`           // /sys/class/net/<iface>/addr_len
-	Address          string `fileName:"address"`            // /sys/class/net/<iface>/address
-	Broadcast        string `fileName:"broadcast"`          // /sys/class/net/<iface>/broadcast
-	Carrier          *int64 `fileName:"carrier"`            // /sys/class/net/<iface>/carrier
-	CarrierChanges   *int64 `fileName:"carrier_changes"`    // /sys/class/net/<iface>/carrier_changes
-	CarrierUpCount   *int64 `fileName:"carrier_up_count"`   // /sys/class/net/<iface>/carrier_up_count
-	CarrierDownCount *int64 `fileName:"carrier_down_count"` // /sys/class/net/<iface>/carrier_down_count
-	DevID            *int64 `fileName:"dev_id"`             // /sys/class/net/<iface>/dev_id
-	Dormant          *int64 `fileName:"dormant"`            // /sys/class/net/<iface>/dormant
-	Duplex           string `fileName:"duplex"`             // /sys/class/net/<iface>/duplex
-	Flags            *int64 `fileName:"flags"`              // /sys/class/net/<iface>/flags
-	IfAlias          string `fileName:"ifalias"`            // /sys/class/net/<iface>/ifalias
-	IfIndex          *int64 `fileName:"ifindex"`            // /sys/class/net/<iface>/ifindex
-	IfLink           *int64 `fileName:"iflink"`             // /sys/class/net/<iface>/iflink
-	LinkMode         *int64 `fileName:"link_mode"`          // /sys/class/net/<iface>/link_mode
-	MTU              *int64 `fileName:"mtu"`                // /sys/class/net/<iface>/mtu
-	NameAssignType   *int64 `fileName:"name_assign_type"`   // /sys/class/net/<iface>/name_assign_type
-	NetDevGroup      *int64 `fileName:"netdev_group"`       // /sys/class/net/<iface>/netdev_group
-	OperState        string `fileName:"operstate"`          // /sys/class/net/<iface>/operstate
-	PhysPortID       string `fileName:"phys_port_id"`       // /sys/class/net/<iface>/phys_port_id
-	PhysPortName     string `fileName:"phys_port_name"`     // /sys/class/net/<iface>/phys_port_name
-	PhysSwitchID     string `fileName:"phys_switch_id"`     // /sys/class/net/<iface>/phys_switch_id
-	Speed            *int64 `fileName:"speed"`              // /sys/class/net/<iface>/speed
-	TxQueueLen       *int64 `fileName:"tx_queue_len"`       // /sys/class/net/<iface>/tx_queue_len
-	Type             *int64 `fileName:"type"`               // /sys/class/net/<iface>/type
+	AddrAssignType   *int64 // /sys/class/net/<iface>/addr_assign_type
+	AddrLen          *int64 // /sys/class/net/<iface>/addr_len
+	Address          string // /sys/class/net/<iface>/address
+	Broadcast        string // /sys/class/net/<iface>/broadcast
+	Carrier          *int64 // /sys/class/net/<iface>/carrier
+	CarrierChanges   *int64 // /sys/class/net/<iface>/carrier_changes
+	CarrierUpCount   *int64 // /sys/class/net/<iface>/carrier_up_count
+	CarrierDownCount *int64 // /sys/class/net/<iface>/carrier_down_count
+	DevID            *int64 // /sys/class/net/<iface>/dev_id
+	Dormant          *int64 // /sys/class/net/<iface>/dormant
+	Duplex           string // /sys/class/net/<iface>/duplex
+	Flags            *int64 // /sys/class/net/<iface>/flags
+	IfAlias          string // /sys/class/net/<iface>/ifalias
+	IfIndex          *int64 // /sys/class/net/<iface>/ifindex
+	IfLink           *int64 // /sys/class/net/<iface>/iflink
+	LinkMode         *int64 // /sys/class/net/<iface>/link_mode
+	MTU              *int64 // /sys/class/net/<iface>/mtu
+	NameAssignType   *int64 // /sys/class/net/<iface>/name_assign_type
+	NetDevGroup      *int64 // /sys/class/net/<iface>/netdev_group
+	OperState        string // /sys/class/net/<iface>/operstate
+	PhysPortID       string // /sys/class/net/<iface>/phys_port_id
+	PhysPortName     string // /sys/class/net/<iface>/phys_port_name
+	PhysSwitchID     string // /sys/class/net/<iface>/phys_switch_id
+	Speed            *int64 // /sys/class/net/<iface>/speed
+	TxQueueLen       *int64 // /sys/class/net/<iface>/tx_queue_len
+	Type             *int64 // /sys/class/net/<iface>/type
 }
 
 // NetClass is collection of info for every interface (iface) in /sys/class/net. The map keys
@@ -109,54 +105,77 @@ func (fs FS) NetClass() (NetClass, error) {
 // directory and gets their contents.
 func (nc NetClass) parseNetClassIface(devicePath string) (*NetClassIface, error) {
 	interfaceClass := NetClassIface{}
-	interfaceElem := reflect.ValueOf(&interfaceClass).Elem()
-	interfaceType := reflect.TypeOf(interfaceClass)
 
-	//start from 1 - skip the Name field
-	for i := 1; i < interfaceElem.NumField(); i++ {
-		fieldType := interfaceType.Field(i)
-		fieldValue := interfaceElem.Field(i)
+	files, err := ioutil.ReadDir(devicePath)
+	if err != nil {
+		return nil, err
+	}
 
-		if fieldType.Tag.Get("fileName") == "" {
-			panic(fmt.Errorf("field %s does not have a filename tag", fieldType.Name))
+	for _, f := range files {
+		if f.IsDir() {
+			continue
 		}
-
-		value, err := util.SysReadFile(devicePath + "/" + fieldType.Tag.Get("fileName"))
-
+		name := filepath.Join(devicePath, f.Name())
+		value, err := util.SysReadFile(name)
 		if err != nil {
-			if os.IsNotExist(err) || err.Error() == "operation not supported" || err.Error() == "invalid argument" {
-				continue
-			}
-			return nil, fmt.Errorf("could not access file %s: %s", fieldType.Tag.Get("fileName"), err)
+			return nil, fmt.Errorf("failed to read file %q: %v", name, err)
 		}
-
-		switch fieldValue.Kind() {
-		case reflect.String:
-			fieldValue.SetString(value)
-		case reflect.Ptr:
-			var int64ptr *int64
-			switch fieldValue.Type() {
-			case reflect.TypeOf(int64ptr):
-				var intValue int64
-				if strings.HasPrefix(value, "0x") {
-					intValue, err = strconv.ParseInt(value[2:], 16, 64)
-					if err != nil {
-						return nil, fmt.Errorf("expected hex value for %s, got: %s", fieldType.Name, value)
-					}
-				} else {
-					intValue, err = strconv.ParseInt(value, 10, 64)
-					if err != nil {
-						return nil, fmt.Errorf("expected Uint64 value for %s, got: %s", fieldType.Name, value)
-					}
-				}
-				fieldValue.Set(reflect.ValueOf(&intValue))
-			default:
-				return nil, fmt.Errorf("unhandled pointer type %q", fieldValue.Type())
-			}
-		default:
-			return nil, fmt.Errorf("unhandled type %q", fieldValue.Kind())
+		vp := util.NewValueParser(value)
+		switch f.Name() {
+		case "addr_assign_type":
+			interfaceClass.AddrAssignType = vp.PInt64()
+		case "addr_len":
+			interfaceClass.AddrLen = vp.PInt64()
+		case "address":
+			interfaceClass.Address = value
+		case "broadcast":
+			interfaceClass.Broadcast = value
+		case "carrier":
+			interfaceClass.Carrier = vp.PInt64()
+		case "carrier_changes":
+			interfaceClass.CarrierChanges = vp.PInt64()
+		case "carrier_up_count":
+			interfaceClass.CarrierUpCount = vp.PInt64()
+		case "carrier_down_count":
+			interfaceClass.CarrierDownCount = vp.PInt64()
+		case "dev_id":
+			interfaceClass.DevID = vp.PInt64()
+		case "dormant":
+			interfaceClass.Dormant = vp.PInt64()
+		case "duplex":
+			interfaceClass.Duplex = value
+		case "flags":
+			interfaceClass.Flags = vp.PInt64()
+		case "ifalias":
+			interfaceClass.IfAlias = value
+		case "ifindex":
+			interfaceClass.IfIndex = vp.PInt64()
+		case "iflink":
+			interfaceClass.IfLink = vp.PInt64()
+		case "link_mode":
+			interfaceClass.LinkMode = vp.PInt64()
+		case "mtu":
+			interfaceClass.MTU = vp.PInt64()
+		case "name_assign_type":
+			interfaceClass.NameAssignType = vp.PInt64()
+		case "netdev_group":
+			interfaceClass.NetDevGroup = vp.PInt64()
+		case "operstate":
+			interfaceClass.OperState = value
+		case "phys_port_id":
+			interfaceClass.PhysPortID = value
+		case "phys_port_name":
+			interfaceClass.PhysPortName = value
+		case "phys_switch_id":
+			interfaceClass.PhysSwitchID = value
+		case "speed":
+			interfaceClass.Speed = vp.PInt64()
+		case "tx_queue_len":
+			interfaceClass.TxQueueLen = vp.PInt64()
+		case "type":
+			interfaceClass.Type = vp.PInt64()
 		}
 	}
-
 	return &interfaceClass, nil
+
 }
diff --git a/sysfs/vulnerability.go b/sysfs/vulnerability.go
new file mode 100644
index 0000000..d41ecfb
--- /dev/null
+++ b/sysfs/vulnerability.go
@@ -0,0 +1,84 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sysfs
+
+import (
+	"fmt"
+	"io/ioutil"
+	"path/filepath"
+	"strings"
+)
+
+const (
+	notAffected = "Not Affected"
+	vulnerable  = "Vulnerable"
+	mitigation  = "Mitigation"
+)
+
+// CPUVulnerabilities retrieves a map of vulnerability names to their mitigations.
+func (fs FS) CPUVulnerabilities() ([]Vulnerability, error) {
+	matches, err := filepath.Glob(fs.sys.Path("devices/system/cpu/vulnerabilities/*"))
+	if err != nil {
+		return nil, err
+	}
+
+	vulnerabilities := make([]Vulnerability, 0, len(matches))
+	for _, match := range matches {
+		name := filepath.Base(match)
+
+		value, err := ioutil.ReadFile(match)
+		if err != nil {
+			return nil, err
+		}
+
+		v, err := parseVulnerability(name, string(value))
+		if err != nil {
+			return nil, err
+		}
+
+		vulnerabilities = append(vulnerabilities, v)
+	}
+
+	return vulnerabilities, nil
+}
+
+// Vulnerability represents a single vulnerability extracted from /sys/devices/system/cpu/vulnerabilities/
+type Vulnerability struct {
+	CodeName   string
+	State      string
+	Mitigation string
+}
+
+func parseVulnerability(name, value string) (Vulnerability, error) {
+	v := Vulnerability{CodeName: name}
+	value = strings.TrimSpace(value)
+	if value == notAffected {
+		v.State = notAffected
+		return v, nil
+	}
+
+	if strings.HasPrefix(value, vulnerable) {
+		v.State = vulnerable
+		v.Mitigation = strings.TrimPrefix(strings.TrimPrefix(value, vulnerable), ": ")
+		return v, nil
+	}
+
+	if strings.HasPrefix(value, mitigation) {
+		v.State = mitigation
+		v.Mitigation = strings.TrimPrefix(strings.TrimPrefix(value, mitigation), ": ")
+		return v, nil
+	}
+
+	return v, fmt.Errorf("unknown vulnerability state for %s: %s", name, value)
+}
diff --git a/vm.go b/vm.go
index f22651c..cb13891 100644
--- a/vm.go
+++ b/vm.go
@@ -31,49 +31,49 @@ import (
 // Each file contains one line with a single numerical value, except lowmem_reserve_ratio which holds an array
 // and numa_zonelist_order (deprecated) which is a string
 type VM struct {
-	AdminReserveKbytes        int64   // /proc/sys/vm/admin_reserve_kbytes
-	BlockDump                 int64   // /proc/sys/vm/block_dump
-	CompactUnevictableAllowed int64   // /proc/sys/vm/compact_unevictable_allowed
-	DirtyBackgroundBytes      int64   // /proc/sys/vm/dirty_background_bytes
-	DirtyBackgroundRatio      int64   // /proc/sys/vm/dirty_background_ratio
-	DirtyBytes                int64   // /proc/sys/vm/dirty_bytes
-	DirtyExpireCentisecs      int64   // /proc/sys/vm/dirty_expire_centisecs
-	DirtyRatio                int64   // /proc/sys/vm/dirty_ratio
-	DirtytimeExpireSeconds    int64   // /proc/sys/vm/dirtytime_expire_seconds
-	DirtyWritebackCentisecs   int64   // /proc/sys/vm/dirty_writeback_centisecs
-	DropCaches                int64   // /proc/sys/vm/drop_caches
-	ExtfragThreshold          int64   // /proc/sys/vm/extfrag_threshold
-	HugetlbShmGroup           int64   // /proc/sys/vm/hugetlb_shm_group
-	LaptopMode                int64   // /proc/sys/vm/laptop_mode
-	LegacyVaLayout            int64   // /proc/sys/vm/legacy_va_layout
-	LowmemReserveRatio        []int64 // /proc/sys/vm/lowmem_reserve_ratio
-	MaxMapCount               int64   // /proc/sys/vm/max_map_count
-	MemoryFailureEarlyKill    int64   // /proc/sys/vm/memory_failure_early_kill
-	MemoryFailureRecovery     int64   // /proc/sys/vm/memory_failure_recovery
-	MinFreeKbytes             int64   // /proc/sys/vm/min_free_kbytes
-	MinSlabRatio              int64   // /proc/sys/vm/min_slab_ratio
-	MinUnmappedRatio          int64   // /proc/sys/vm/min_unmapped_ratio
-	MmapMinAddr               int64   // /proc/sys/vm/mmap_min_addr
-	NrHugepages               int64   // /proc/sys/vm/nr_hugepages
-	NrHugepagesMempolicy      int64   // /proc/sys/vm/nr_hugepages_mempolicy
-	NrOvercommitHugepages     int64   // /proc/sys/vm/nr_overcommit_hugepages
-	NumaStat                  int64   // /proc/sys/vm/numa_stat
-	NumaZonelistOrder         string  // /proc/sys/vm/numa_zonelist_order
-	OomDumpTasks              int64   // /proc/sys/vm/oom_dump_tasks
-	OomKillAllocatingTask     int64   // /proc/sys/vm/oom_kill_allocating_task
-	OvercommitKbytes          int64   // /proc/sys/vm/overcommit_kbytes
-	OvercommitMemory          int64   // /proc/sys/vm/overcommit_memory
-	OvercommitRatio           int64   // /proc/sys/vm/overcommit_ratio
-	PageCluster               int64   // /proc/sys/vm/page-cluster
-	PanicOnOom                int64   // /proc/sys/vm/panic_on_oom
-	PercpuPagelistFraction    int64   // /proc/sys/vm/percpu_pagelist_fraction
-	StatInterval              int64   // /proc/sys/vm/stat_interval
-	Swappiness                int64   // /proc/sys/vm/swappiness
-	UserReserveKbytes         int64   // /proc/sys/vm/user_reserve_kbytes
-	VfsCachePressure          int64   // /proc/sys/vm/vfs_cache_pressure
-	WatermarkBoostFactor      int64   // /proc/sys/vm/watermark_boost_factor
-	WatermarkScaleFactor      int64   // /proc/sys/vm/watermark_scale_factor
-	ZoneReclaimMode           int64   // /proc/sys/vm/zone_reclaim_mode
+	AdminReserveKbytes        *int64   // /proc/sys/vm/admin_reserve_kbytes
+	BlockDump                 *int64   // /proc/sys/vm/block_dump
+	CompactUnevictableAllowed *int64   // /proc/sys/vm/compact_unevictable_allowed
+	DirtyBackgroundBytes      *int64   // /proc/sys/vm/dirty_background_bytes
+	DirtyBackgroundRatio      *int64   // /proc/sys/vm/dirty_background_ratio
+	DirtyBytes                *int64   // /proc/sys/vm/dirty_bytes
+	DirtyExpireCentisecs      *int64   // /proc/sys/vm/dirty_expire_centisecs
+	DirtyRatio                *int64   // /proc/sys/vm/dirty_ratio
+	DirtytimeExpireSeconds    *int64   // /proc/sys/vm/dirtytime_expire_seconds
+	DirtyWritebackCentisecs   *int64   // /proc/sys/vm/dirty_writeback_centisecs
+	DropCaches                *int64   // /proc/sys/vm/drop_caches
+	ExtfragThreshold          *int64   // /proc/sys/vm/extfrag_threshold
+	HugetlbShmGroup           *int64   // /proc/sys/vm/hugetlb_shm_group
+	LaptopMode                *int64   // /proc/sys/vm/laptop_mode
+	LegacyVaLayout            *int64   // /proc/sys/vm/legacy_va_layout
+	LowmemReserveRatio        []*int64 // /proc/sys/vm/lowmem_reserve_ratio
+	MaxMapCount               *int64   // /proc/sys/vm/max_map_count
+	MemoryFailureEarlyKill    *int64   // /proc/sys/vm/memory_failure_early_kill
+	MemoryFailureRecovery     *int64   // /proc/sys/vm/memory_failure_recovery
+	MinFreeKbytes             *int64   // /proc/sys/vm/min_free_kbytes
+	MinSlabRatio              *int64   // /proc/sys/vm/min_slab_ratio
+	MinUnmappedRatio          *int64   // /proc/sys/vm/min_unmapped_ratio
+	MmapMinAddr               *int64   // /proc/sys/vm/mmap_min_addr
+	NrHugepages               *int64   // /proc/sys/vm/nr_hugepages
+	NrHugepagesMempolicy      *int64   // /proc/sys/vm/nr_hugepages_mempolicy
+	NrOvercommitHugepages     *int64   // /proc/sys/vm/nr_overcommit_hugepages
+	NumaStat                  *int64   // /proc/sys/vm/numa_stat
+	NumaZonelistOrder         string   // /proc/sys/vm/numa_zonelist_order
+	OomDumpTasks              *int64   // /proc/sys/vm/oom_dump_tasks
+	OomKillAllocatingTask     *int64   // /proc/sys/vm/oom_kill_allocating_task
+	OvercommitKbytes          *int64   // /proc/sys/vm/overcommit_kbytes
+	OvercommitMemory          *int64   // /proc/sys/vm/overcommit_memory
+	OvercommitRatio           *int64   // /proc/sys/vm/overcommit_ratio
+	PageCluster               *int64   // /proc/sys/vm/page-cluster
+	PanicOnOom                *int64   // /proc/sys/vm/panic_on_oom
+	PercpuPagelistFraction    *int64   // /proc/sys/vm/percpu_pagelist_fraction
+	StatInterval              *int64   // /proc/sys/vm/stat_interval
+	Swappiness                *int64   // /proc/sys/vm/swappiness
+	UserReserveKbytes         *int64   // /proc/sys/vm/user_reserve_kbytes
+	VfsCachePressure          *int64   // /proc/sys/vm/vfs_cache_pressure
+	WatermarkBoostFactor      *int64   // /proc/sys/vm/watermark_boost_factor
+	WatermarkScaleFactor      *int64   // /proc/sys/vm/watermark_scale_factor
+	ZoneReclaimMode           *int64   // /proc/sys/vm/zone_reclaim_mode
 }
 
 // VM reads the VM statistics from the specified `proc` filesystem.
@@ -109,97 +109,97 @@ func (fs FS) VM() (*VM, error) {
 
 		switch f.Name() {
 		case "admin_reserve_kbytes":
-			vm.AdminReserveKbytes = *vp.PInt64()
+			vm.AdminReserveKbytes = vp.PInt64()
 		case "block_dump":
-			vm.BlockDump = *vp.PInt64()
+			vm.BlockDump = vp.PInt64()
 		case "compact_unevictable_allowed":
-			vm.CompactUnevictableAllowed = *vp.PInt64()
+			vm.CompactUnevictableAllowed = vp.PInt64()
 		case "dirty_background_bytes":
-			vm.DirtyBackgroundBytes = *vp.PInt64()
+			vm.DirtyBackgroundBytes = vp.PInt64()
 		case "dirty_background_ratio":
-			vm.DirtyBackgroundRatio = *vp.PInt64()
+			vm.DirtyBackgroundRatio = vp.PInt64()
 		case "dirty_bytes":
-			vm.DirtyBytes = *vp.PInt64()
+			vm.DirtyBytes = vp.PInt64()
 		case "dirty_expire_centisecs":
-			vm.DirtyExpireCentisecs = *vp.PInt64()
+			vm.DirtyExpireCentisecs = vp.PInt64()
 		case "dirty_ratio":
-			vm.DirtyRatio = *vp.PInt64()
+			vm.DirtyRatio = vp.PInt64()
 		case "dirtytime_expire_seconds":
-			vm.DirtytimeExpireSeconds = *vp.PInt64()
+			vm.DirtytimeExpireSeconds = vp.PInt64()
 		case "dirty_writeback_centisecs":
-			vm.DirtyWritebackCentisecs = *vp.PInt64()
+			vm.DirtyWritebackCentisecs = vp.PInt64()
 		case "drop_caches":
-			vm.DropCaches = *vp.PInt64()
+			vm.DropCaches = vp.PInt64()
 		case "extfrag_threshold":
-			vm.ExtfragThreshold = *vp.PInt64()
+			vm.ExtfragThreshold = vp.PInt64()
 		case "hugetlb_shm_group":
-			vm.HugetlbShmGroup = *vp.PInt64()
+			vm.HugetlbShmGroup = vp.PInt64()
 		case "laptop_mode":
-			vm.LaptopMode = *vp.PInt64()
+			vm.LaptopMode = vp.PInt64()
 		case "legacy_va_layout":
-			vm.LegacyVaLayout = *vp.PInt64()
+			vm.LegacyVaLayout = vp.PInt64()
 		case "lowmem_reserve_ratio":
 			stringSlice := strings.Fields(value)
-			int64Slice := make([]int64, 0, len(stringSlice))
+			pint64Slice := make([]*int64, 0, len(stringSlice))
 			for _, value := range stringSlice {
 				vp := util.NewValueParser(value)
-				int64Slice = append(int64Slice, *vp.PInt64())
+				pint64Slice = append(pint64Slice, vp.PInt64())
 			}
-			vm.LowmemReserveRatio = int64Slice
+			vm.LowmemReserveRatio = pint64Slice
 		case "max_map_count":
-			vm.MaxMapCount = *vp.PInt64()
+			vm.MaxMapCount = vp.PInt64()
 		case "memory_failure_early_kill":
-			vm.MemoryFailureEarlyKill = *vp.PInt64()
+			vm.MemoryFailureEarlyKill = vp.PInt64()
 		case "memory_failure_recovery":
-			vm.MemoryFailureRecovery = *vp.PInt64()
+			vm.MemoryFailureRecovery = vp.PInt64()
 		case "min_free_kbytes":
-			vm.MinFreeKbytes = *vp.PInt64()
+			vm.MinFreeKbytes = vp.PInt64()
 		case "min_slab_ratio":
-			vm.MinSlabRatio = *vp.PInt64()
+			vm.MinSlabRatio = vp.PInt64()
 		case "min_unmapped_ratio":
-			vm.MinUnmappedRatio = *vp.PInt64()
+			vm.MinUnmappedRatio = vp.PInt64()
 		case "mmap_min_addr":
-			vm.MmapMinAddr = *vp.PInt64()
+			vm.MmapMinAddr = vp.PInt64()
 		case "nr_hugepages":
-			vm.NrHugepages = *vp.PInt64()
+			vm.NrHugepages = vp.PInt64()
 		case "nr_hugepages_mempolicy":
-			vm.NrHugepagesMempolicy = *vp.PInt64()
+			vm.NrHugepagesMempolicy = vp.PInt64()
 		case "nr_overcommit_hugepages":
-			vm.NrOvercommitHugepages = *vp.PInt64()
+			vm.NrOvercommitHugepages = vp.PInt64()
 		case "numa_stat":
-			vm.NumaStat = *vp.PInt64()
+			vm.NumaStat = vp.PInt64()
 		case "numa_zonelist_order":
 			vm.NumaZonelistOrder = value
 		case "oom_dump_tasks":
-			vm.OomDumpTasks = *vp.PInt64()
+			vm.OomDumpTasks = vp.PInt64()
 		case "oom_kill_allocating_task":
-			vm.OomKillAllocatingTask = *vp.PInt64()
+			vm.OomKillAllocatingTask = vp.PInt64()
 		case "overcommit_kbytes":
-			vm.OvercommitKbytes = *vp.PInt64()
+			vm.OvercommitKbytes = vp.PInt64()
 		case "overcommit_memory":
-			vm.OvercommitMemory = *vp.PInt64()
+			vm.OvercommitMemory = vp.PInt64()
 		case "overcommit_ratio":
-			vm.OvercommitRatio = *vp.PInt64()
+			vm.OvercommitRatio = vp.PInt64()
 		case "page-cluster":
-			vm.PageCluster = *vp.PInt64()
+			vm.PageCluster = vp.PInt64()
 		case "panic_on_oom":
-			vm.PanicOnOom = *vp.PInt64()
+			vm.PanicOnOom = vp.PInt64()
 		case "percpu_pagelist_fraction":
-			vm.PercpuPagelistFraction = *vp.PInt64()
+			vm.PercpuPagelistFraction = vp.PInt64()
 		case "stat_interval":
-			vm.StatInterval = *vp.PInt64()
+			vm.StatInterval = vp.PInt64()
 		case "swappiness":
-			vm.Swappiness = *vp.PInt64()
+			vm.Swappiness = vp.PInt64()
 		case "user_reserve_kbytes":
-			vm.UserReserveKbytes = *vp.PInt64()
+			vm.UserReserveKbytes = vp.PInt64()
 		case "vfs_cache_pressure":
-			vm.VfsCachePressure = *vp.PInt64()
+			vm.VfsCachePressure = vp.PInt64()
 		case "watermark_boost_factor":
-			vm.WatermarkBoostFactor = *vp.PInt64()
+			vm.WatermarkBoostFactor = vp.PInt64()
 		case "watermark_scale_factor":
-			vm.WatermarkScaleFactor = *vp.PInt64()
+			vm.WatermarkScaleFactor = vp.PInt64()
 		case "zone_reclaim_mode":
-			vm.ZoneReclaimMode = *vp.PInt64()
+			vm.ZoneReclaimMode = vp.PInt64()
 		}
 		if err := vp.Err(); err != nil {
 			return nil, err
diff --git a/xfs/parse.go b/xfs/parse.go
index b3d8634..9781446 100644
--- a/xfs/parse.go
+++ b/xfs/parse.go
@@ -35,23 +35,25 @@ func ParseStats(r io.Reader) (*Stats, error) {
 		fieldTrans       = "trans"
 		fieldIg          = "ig"
 		fieldLog         = "log"
+		fieldPushAil     = "push_ail"
+		fieldXstrat      = "xstrat"
 		fieldRw          = "rw"
 		fieldAttr        = "attr"
 		fieldIcluster    = "icluster"
 		fieldVnodes      = "vnodes"
 		fieldBuf         = "buf"
 		fieldXpc         = "xpc"
-
+		fieldAbtb2       = "abtb2"
+		fieldAbtc2       = "abtc2"
+		fieldBmbt2       = "bmbt2"
+		fieldIbt2        = "ibt2"
+		//fieldFibt2 = "fibt2"
+		fieldQm    = "qm"
+		fieldDebug = "debug"
 		// Unimplemented at this time due to lack of documentation.
-		// fieldPushAil = "push_ail"
-		// fieldXstrat  = "xstrat"
-		// fieldAbtb2   = "abtb2"
-		// fieldAbtc2   = "abtc2"
-		// fieldBmbt2   = "bmbt2"
-		// fieldIbt2    = "ibt2"
-		// fieldFibt2   = "fibt2"
-		// fieldQm      = "qm"
-		// fieldDebug   = "debug"
+		//fieldRmapbt = "rmapbt"
+		//fieldRefcntbt = "refcntbt"
+
 	)
 
 	var xfss Stats
@@ -115,6 +117,23 @@ func ParseStats(r io.Reader) (*Stats, error) {
 			xfss.Vnode, err = vnodeStats(us)
 		case fieldBuf:
 			xfss.Buffer, err = bufferStats(us)
+		case fieldPushAil:
+			xfss.PushAil, err = pushAilStats(us)
+		case fieldXstrat:
+			xfss.Xstrat, err = xStratStats(us)
+		case fieldAbtb2:
+			xfss.BtreeAllocBlocks2, err = btreeAllocBlocks2Stats(us)
+		case fieldAbtc2:
+			xfss.BtreeAllocContig2, err = btreeAllocContig2Stats(us)
+		case fieldBmbt2:
+			xfss.BtreeBlockMap2, err = btreeBlockMap2Stats(us)
+		case fieldIbt2:
+			xfss.BtreeInode2, err = btreeInode2Stats(us)
+		//case fieldFibt2:
+		case fieldQm:
+			xfss.QuotaManager, err = quotaManagerStats(us)
+		case fieldDebug:
+			xfss.Debug, err = debugStats(us)
 		}
 		if err != nil {
 			return nil, err
@@ -228,7 +247,39 @@ func logOperationStats(us []uint32) (LogOperationStats, error) {
 	}, nil
 }
 
-// ReadWriteStats builds a ReadWriteStats from a slice of uint32s.
+// push_ail
+func pushAilStats(us []uint32) (PushAilStats, error) {
+	if l := len(us); l != 10 {
+		return PushAilStats{}, fmt.Errorf("incorrect number of values for XFS push ail stats: %d", l)
+	}
+
+	return PushAilStats{
+		TryLogspace:   us[0],
+		SleepLogspace: us[1],
+		Pushes:        us[2],
+		Success:       us[3],
+		PushBuf:       us[4],
+		Pinned:        us[5],
+		Locked:        us[6],
+		Flushing:      us[7],
+		Restarts:      us[8],
+		Flush:         us[9],
+	}, nil
+}
+
+// xstrat
+func xStratStats(us []uint32) (XstratStats, error) {
+	if l := len(us); l != 2 {
+		return XstratStats{}, fmt.Errorf("incorrect number of values for XFS  xstrat stats: %d", l)
+	}
+
+	return XstratStats{
+		Quick: us[0],
+		Split: us[1],
+	}, nil
+}
+
+// rw
 func readWriteStats(us []uint32) (ReadWriteStats, error) {
 	if l := len(us); l != 2 {
 		return ReadWriteStats{}, fmt.Errorf("incorrect number of values for XFS read write stats: %d", l)
@@ -328,3 +379,130 @@ func extendedPrecisionStats(us []uint64) (ExtendedPrecisionStats, error) {
 		ReadBytes:  us[2],
 	}, nil
 }
+
+func quotaManagerStats(us []uint32) (QuotaManagerStats, error) {
+	if l := len(us); l != 8 {
+		return QuotaManagerStats{}, fmt.Errorf("incorrect number of values for XFS quota stats: %d", l)
+	}
+
+	return QuotaManagerStats{
+		Reclaims:      us[0],
+		ReclaimMisses: us[1],
+		DquoteDups:    us[2],
+		CacheMisses:   us[3],
+		CacheHits:     us[4],
+		Wants:         us[5],
+		ShakeReclaims: us[6],
+		InactReclaims: us[7],
+	}, nil
+}
+
+func debugStats(us []uint32) (DebugStats, error) {
+	if l := len(us); l != 1 {
+		return DebugStats{}, fmt.Errorf("incorrect number of values for XFS debug stats: %d", l)
+	}
+
+	return DebugStats{
+		Enabled: us[0],
+	}, nil
+}
+
+// abtb2
+func btreeAllocBlocks2Stats(us []uint32) (BtreeAllocBlocks2Stats, error) {
+	if l := len(us); l != 15 {
+		return BtreeAllocBlocks2Stats{}, fmt.Errorf("incorrect number of values for abtb2 stats: %d", 1)
+	}
+
+	return BtreeAllocBlocks2Stats{
+		Lookup:    us[0],
+		Compare:   us[1],
+		Insrec:    us[2],
+		Delrec:    us[3],
+		NewRoot:   us[4],
+		KillRoot:  us[5],
+		Increment: us[6],
+		Decrement: us[7],
+		Lshift:    us[8],
+		Rshift:    us[9],
+		Split:     us[10],
+		Join:      us[11],
+		Alloc:     us[12],
+		Free:      us[13],
+		Moves:     us[14],
+	}, nil
+}
+
+// abtc2
+func btreeAllocContig2Stats(us []uint32) (BtreeAllocContig2Stats, error) {
+	if l := len(us); l != 15 {
+		return BtreeAllocContig2Stats{}, fmt.Errorf("incorrect number of values for abtc2 stats: %d", 1)
+	}
+
+	return BtreeAllocContig2Stats{
+		Lookup:    us[0],
+		Compare:   us[1],
+		Insrec:    us[2],
+		Delrec:    us[3],
+		NewRoot:   us[4],
+		KillRoot:  us[5],
+		Increment: us[6],
+		Decrement: us[7],
+		Lshift:    us[8],
+		Rshift:    us[9],
+		Split:     us[10],
+		Join:      us[11],
+		Alloc:     us[12],
+		Free:      us[13],
+		Moves:     us[14],
+	}, nil
+}
+
+// bmbt2
+func btreeBlockMap2Stats(us []uint32) (BtreeBlockMap2Stats, error) {
+	if l := len(us); l != 15 {
+		return BtreeBlockMap2Stats{}, fmt.Errorf("incorrect number of values for bmbt2 stats: %d", 1)
+	}
+
+	return BtreeBlockMap2Stats{
+		Lookup:    us[0],
+		Compare:   us[1],
+		Insrec:    us[2],
+		Delrec:    us[3],
+		NewRoot:   us[4],
+		KillRoot:  us[5],
+		Increment: us[6],
+		Decrement: us[7],
+		Lshift:    us[8],
+		Rshift:    us[9],
+		Split:     us[10],
+		Join:      us[11],
+		Alloc:     us[12],
+		Free:      us[13],
+		Moves:     us[14],
+	}, nil
+}
+
+// ibt2
+func btreeInode2Stats(us []uint32) (BtreeInode2Stats, error) {
+	if l := len(us); l != 15 {
+		return BtreeInode2Stats{}, fmt.Errorf("incorrect number of values for ibt2 stats: %d", 1)
+	}
+
+	return BtreeInode2Stats{
+		Lookup:    us[0],
+		Compare:   us[1],
+		Insrec:    us[2],
+		Delrec:    us[3],
+		NewRoot:   us[4],
+		KillRoot:  us[5],
+		Increment: us[6],
+		Decrement: us[7],
+		Lshift:    us[8],
+		Rshift:    us[9],
+		Split:     us[10],
+		Join:      us[11],
+		Alloc:     us[12],
+		Free:      us[13],
+		Moves:     us[14],
+	}, nil
+}
diff --git a/xfs/xfs.go b/xfs/xfs.go
index 3aad661..018a0b8 100644
--- a/xfs/xfs.go
+++ b/xfs/xfs.go
@@ -49,6 +49,14 @@ type Stats struct {
 	Vnode              VnodeStats
 	Buffer             BufferStats
 	ExtendedPrecision  ExtendedPrecisionStats
+	Xstrat             XstratStats            // xstrat
+	PushAil            PushAilStats           // push_ail
+	Debug              DebugStats             // debug
+	QuotaManager       QuotaManagerStats      // qm
+	BtreeAllocBlocks2  BtreeAllocBlocks2Stats // abtb2
+	BtreeAllocContig2  BtreeAllocContig2Stats // abtc2
+	BtreeBlockMap2     BtreeBlockMap2Stats    // bmbt2
+	BtreeInode2        BtreeInode2Stats       // ibt2
 }
 
 // ExtentAllocationStats contains statistics regarding XFS extent allocations.
@@ -170,6 +178,119 @@ type ExtendedPrecisionStats struct {
 	ReadBytes  uint64
 }
 
+// PushAilStats contains statistics on tail-pushing operations.
+type PushAilStats struct {
+	TryLogspace   uint32
+	SleepLogspace uint32
+	Pushes        uint32
+	Success       uint32
+	PushBuf       uint32
+	Pinned        uint32
+	Locked        uint32
+	Flushing      uint32
+	Restarts      uint32
+	Flush         uint32
+}
+
+// QuotaManagerStats contain statistics regarding quota processing.
+type QuotaManagerStats struct {
+	Reclaims      uint32
+	ReclaimMisses uint32
+	DquoteDups    uint32
+	CacheMisses   uint32
+	CacheHits     uint32
+	Wants         uint32
+	ShakeReclaims uint32
+	InactReclaims uint32
+}
+
+// XstratStats contains statistics regarding bytes processed by the XFS daemon.
+type XstratStats struct {
+	Quick uint32
+	Split uint32
+}
+
+// DebugStats indicate if XFS debugging is enabled.
+type DebugStats struct {
+	Enabled uint32
+}
+
+// BtreeAllocBlocks2Stats contains statistics on B-Tree v2 allocations.
+type BtreeAllocBlocks2Stats struct {
+	Lookup    uint32
+	Compare   uint32
+	Insrec    uint32
+	Delrec    uint32
+	NewRoot   uint32
+	KillRoot  uint32
+	Increment uint32
+	Decrement uint32
+	Lshift    uint32
+	Rshift    uint32
+	Split     uint32
+	Join      uint32
+	Alloc     uint32
+	Free      uint32
+	Moves     uint32
+}
+
+// BtreeAllocContig2Stats contain statistics on B-tree v2 free-space-by-size record operations.
+type BtreeAllocContig2Stats struct {
+	Lookup    uint32
+	Compare   uint32
+	Insrec    uint32
+	Delrec    uint32
+	NewRoot   uint32
+	KillRoot  uint32
+	Increment uint32
+	Decrement uint32
+	Lshift    uint32
+	Rshift    uint32
+	Split     uint32
+	Join      uint32
+	Alloc     uint32
+	Free      uint32
+	Moves     uint32
+}
+
+// BtreeBlockMap2Stats contain statistics on B-tree v2 block map operations.
+type BtreeBlockMap2Stats struct {
+	Lookup    uint32
+	Compare   uint32
+	Insrec    uint32
+	Delrec    uint32
+	NewRoot   uint32
+	KillRoot  uint32
+	Increment uint32
+	Decrement uint32
+	Lshift    uint32
+	Rshift    uint32
+	Split     uint32
+	Join      uint32
+	Alloc     uint32
+	Free      uint32
+	Moves     uint32
+}
+
+// BtreeInode2Stats contain statistics on B-tree v2 inode allocations.
+type BtreeInode2Stats struct {
+	Lookup    uint32
+	Compare   uint32
+	Insrec    uint32
+	Delrec    uint32
+	NewRoot   uint32
+	KillRoot  uint32
+	Increment uint32
+	Decrement uint32
+	Lshift    uint32
+	Rshift    uint32
+	Split     uint32
+	Join      uint32
+	Alloc     uint32
+	Free      uint32
+	Moves     uint32
+}
+
 // FS represents the pseudo-filesystems proc and sys, which provides an interface to
 // kernel data structures.
 type FS struct {
diff --git a/zoneinfo.go b/zoneinfo.go
new file mode 100644
index 0000000..e941503
--- /dev/null
+++ b/zoneinfo.go
@@ -0,0 +1,196 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build !windows
+
+package procfs
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"regexp"
+	"strings"
+
+	"github.com/prometheus/procfs/internal/util"
+)
+
+// Zoneinfo holds info parsed from /proc/zoneinfo.
+type Zoneinfo struct {
+	Node                       string
+	Zone                       string
+	NrFreePages                *int64
+	Min                        *int64
+	Low                        *int64
+	High                       *int64
+	Scanned                    *int64
+	Spanned                    *int64
+	Present                    *int64
+	Managed                    *int64
+	NrActiveAnon               *int64
+	NrInactiveAnon             *int64
+	NrIsolatedAnon             *int64
+	NrAnonPages                *int64
+	NrAnonTransparentHugepages *int64
+	NrActiveFile               *int64
+	NrInactiveFile             *int64
+	NrIsolatedFile             *int64
+	NrFilePages                *int64
+	NrSlabReclaimable          *int64
+	NrSlabUnreclaimable        *int64
+	NrMlockStack               *int64
+	NrKernelStack              *int64
+	NrMapped                   *int64
+	NrDirty                    *int64
+	NrWriteback                *int64
+	NrUnevictable              *int64
+	NrShmem                    *int64
+	NrDirtied                  *int64
+	NrWritten                  *int64
+	NumaHit                    *int64
+	NumaMiss                   *int64
+	NumaForeign                *int64
+	NumaInterleave             *int64
+	NumaLocal                  *int64
+	NumaOther                  *int64
+	Protection                 []*int64
+}
+
+var nodeZoneRE = regexp.MustCompile(`(\d+), zone\s+(\w+)`)
+
+// Zoneinfo parses an zoneinfo-file (/proc/zoneinfo) and returns a slice of
+// structs containing the relevant info.  More information available here:
+// https://www.kernel.org/doc/Documentation/sysctl/vm.txt
+func (fs FS) Zoneinfo() ([]Zoneinfo, error) {
+	data, err := ioutil.ReadFile(fs.proc.Path("zoneinfo"))
+	if err != nil {
+		return nil, fmt.Errorf("error reading zoneinfo %s: %s", fs.proc.Path("zoneinfo"), err)
+	}
+	zoneinfo, err := parseZoneinfo(data)
+	if err != nil {
+		return nil, fmt.Errorf("error parsing zoneinfo %s: %s", fs.proc.Path("zoneinfo"), err)
+	}
+	return zoneinfo, nil
+}
+
+func parseZoneinfo(zoneinfoData []byte) ([]Zoneinfo, error) {
+
+	zoneinfo := []Zoneinfo{}
+
+	zoneinfoBlocks := bytes.Split(zoneinfoData, []byte("\nNode"))
+	for _, block := range zoneinfoBlocks {
+		var zoneinfoElement Zoneinfo
+		lines := strings.Split(string(block), "\n")
+		for _, line := range lines {
+
+			if nodeZone := nodeZoneRE.FindStringSubmatch(line); nodeZone != nil {
+				zoneinfoElement.Node = nodeZone[1]
+				zoneinfoElement.Zone = nodeZone[2]
+				continue
+			}
+			if strings.HasPrefix(strings.TrimSpace(line), "per-node stats") {
+				zoneinfoElement.Zone = ""
+				continue
+			}
+			parts := strings.Fields(strings.TrimSpace(line))
+			if len(parts) < 2 {
+				continue
+			}
+			vp := util.NewValueParser(parts[1])
+			switch parts[0] {
+			case "nr_free_pages":
+				zoneinfoElement.NrFreePages = vp.PInt64()
+			case "min":
+				zoneinfoElement.Min = vp.PInt64()
+			case "low":
+				zoneinfoElement.Low = vp.PInt64()
+			case "high":
+				zoneinfoElement.High = vp.PInt64()
+			case "scanned":
+				zoneinfoElement.Scanned = vp.PInt64()
+			case "spanned":
+				zoneinfoElement.Spanned = vp.PInt64()
+			case "present":
+				zoneinfoElement.Present = vp.PInt64()
+			case "managed":
+				zoneinfoElement.Managed = vp.PInt64()
+			case "nr_active_anon":
+				zoneinfoElement.NrActiveAnon = vp.PInt64()
+			case "nr_inactive_anon":
+				zoneinfoElement.NrInactiveAnon = vp.PInt64()
+			case "nr_isolated_anon":
+				zoneinfoElement.NrIsolatedAnon = vp.PInt64()
+			case "nr_anon_pages":
+				zoneinfoElement.NrAnonPages = vp.PInt64()
+			case "nr_anon_transparent_hugepages":
+				zoneinfoElement.NrAnonTransparentHugepages = vp.PInt64()
+			case "nr_active_file":
+				zoneinfoElement.NrActiveFile = vp.PInt64()
+			case "nr_inactive_file":
+				zoneinfoElement.NrInactiveFile = vp.PInt64()
+			case "nr_isolated_file":
+				zoneinfoElement.NrIsolatedFile = vp.PInt64()
+			case "nr_file_pages":
+				zoneinfoElement.NrFilePages = vp.PInt64()
+			case "nr_slab_reclaimable":
+				zoneinfoElement.NrSlabReclaimable = vp.PInt64()
+			case "nr_slab_unreclaimable":
+				zoneinfoElement.NrSlabUnreclaimable = vp.PInt64()
+			case "nr_mlock_stack":
+				zoneinfoElement.NrMlockStack = vp.PInt64()
+			case "nr_kernel_stack":
+				zoneinfoElement.NrKernelStack = vp.PInt64()
+			case "nr_mapped":
+				zoneinfoElement.NrMapped = vp.PInt64()
+			case "nr_dirty":
+				zoneinfoElement.NrDirty = vp.PInt64()
+			case "nr_writeback":
+				zoneinfoElement.NrWriteback = vp.PInt64()
+			case "nr_unevictable":
+				zoneinfoElement.NrUnevictable = vp.PInt64()
+			case "nr_shmem":
+				zoneinfoElement.NrShmem = vp.PInt64()
+			case "nr_dirtied":
+				zoneinfoElement.NrDirtied = vp.PInt64()
+			case "nr_written":
+				zoneinfoElement.NrWritten = vp.PInt64()
+			case "numa_hit":
+				zoneinfoElement.NumaHit = vp.PInt64()
+			case "numa_miss":
+				zoneinfoElement.NumaMiss = vp.PInt64()
+			case "numa_foreign":
+				zoneinfoElement.NumaForeign = vp.PInt64()
+			case "numa_interleave":
+				zoneinfoElement.NumaInterleave = vp.PInt64()
+			case "numa_local":
+				zoneinfoElement.NumaLocal = vp.PInt64()
+			case "numa_other":
+				zoneinfoElement.NumaOther = vp.PInt64()
+			case "protection:":
+				protectionParts := strings.Split(line, ":")
+				protectionValues := strings.Replace(protectionParts[1], "(", "", 1)
+				protectionValues = strings.Replace(protectionValues, ")", "", 1)
+				protectionValues = strings.TrimSpace(protectionValues)
+				protectionStringMap := strings.Split(protectionValues, ", ")
+				val, err := util.ParsePInt64s(protectionStringMap)
+				if err == nil {
+					zoneinfoElement.Protection = val
+				}
+			}
+
+		}
+
+		zoneinfo = append(zoneinfo, zoneinfoElement)
+	}
+	return zoneinfo, nil
+}


Test patch:
diff --git a/arp_test.go b/arp_test.go
new file mode 100644
index 0000000..272567b
--- /dev/null
+++ b/arp_test.go
@@ -0,0 +1,43 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+	"net"
+	"testing"
+)
+
+func TestARP(t *testing.T) {
+	fs, err := NewFS(procTestFixtures)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	arpFile, err := fs.GatherARPEntries()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if want, got := "192.168.224.1", arpFile[0].IPAddr.String(); want != got {
+		t.Errorf("want 192.168.224.1, got %s", got)
+	}
+
+	if want, got := net.HardwareAddr("00:50:56:c0:00:08").String(), arpFile[0].HWAddr.String(); want != got {
+		t.Errorf("want 00:50:56:c0:00:08, got %s", got)
+	}
+
+	if want, got := "ens33", arpFile[0].Device; want != got {
+		t.Errorf("want ens33, got %s", got)
+	}
+}
diff --git a/crypto_test.go b/crypto_test.go
new file mode 100644
index 0000000..4d45824
--- /dev/null
+++ b/crypto_test.go
@@ -0,0 +1,54 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+func newint64(i int64) *int64 {
+	return &i
+}
+func newuint64(i uint64) *uint64 {
+	return &i
+}
+func TestFS_Crypto(t *testing.T) {
+	fs := getProcFixtures(t)
+	crypto, err := fs.Crypto()
+
+	if err != nil {
+		t.Fatalf("parsing of reference-file failed entirely: %s", err)
+	}
+
+	refs := []Crypto{
+		{Name: "ccm(aes)", Driver: "ccm_base(ctr(aes-aesni),cbcmac(aes-aesni))", Module: "ccm", Priority: newint64(300), Refcnt: newint64(4), Selftest: "passed", Internal: "no", Type: "aead", Async: false, Blocksize: newuint64(1), Ivsize: newuint64(16), Maxauthsize: newuint64(16), Geniv: "<none>"},
+		{Name: "cbcmac(aes)", Driver: "cbcmac(aes-aesni)", Module: "ccm", Priority: newint64(300), Refcnt: newint64(7), Selftest: "passed", Internal: "no", Type: "shash", Blocksize: newuint64(1), Digestsize: newuint64(16)},
+		{Name: "ecdh", Driver: "ecdh-generic", Module: "ecdh_generic", Priority: newint64(100), Refcnt: newint64(1), Selftest: "passed", Internal: "no", Type: "kpp"},
+		{Name: "ecb(arc4)", Driver: "ecb(arc4)-generic", Module: "arc4", Priority: newint64(100), Refcnt: newint64(1), Selftest: "passed", Internal: "no", Type: "skcipher", Async: false, Blocksize: newuint64(1), MinKeysize: newuint64(1), MaxKeysize: newuint64(256), Ivsize: newuint64(0), Chunksize: newuint64(1), Walksize: newuint64(1)},
+		{Name: "arc4", Driver: "arc4-generic", Module: "arc4", Priority: newint64(0), Refcnt: newint64(3), Selftest: "passed", Internal: "no", Type: "cipher", Blocksize: newuint64(1), MinKeysize: newuint64(1), MaxKeysize: newuint64(256)},
+		{Name: "crct10dif", Driver: "crct10dif-pclmul", Module: "crct10dif_pclmul", Priority: newint64(200), Refcnt: newint64(2), Selftest: "passed", Internal: "no", Type: "shash", Blocksize: newuint64(1), Digestsize: newuint64(2)},
+	}
+
+	if want, have := len(refs), len(crypto); want > have {
+		t.Errorf("want at least %d parsed crypto-entries, have %d", want, have)
+	}
+	for index, ref := range refs {
+		want, got := ref, crypto[index]
+		if diff := cmp.Diff(want, got); diff != "" {
+			t.Fatalf("unexpected crypto entry (-want +got):\n%s", diff)
+		}
+	}
+}
diff --git a/net_softnet_test.go b/net_softnet_test.go
new file mode 100644
index 0000000..a2658a3
--- /dev/null
+++ b/net_softnet_test.go
@@ -0,0 +1,42 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+	"testing"
+)
+
+func TestSoftnet(t *testing.T) {
+	fs, err := NewFS(procTestFixtures)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	entries, err := fs.GatherSoftnetStats()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if want, got := uint(0x00015c73), entries[0].Processed; want != got {
+		t.Errorf("want %08x, got %08x", want, got)
+	}
+
+	if want, got := uint(0x00020e76), entries[0].Dropped; want != got {
+		t.Errorf("want %08x, got %08x", want, got)
+	}
+
+	if want, got := uint(0xF0000769), entries[0].TimeSqueezed; want != got {
+		t.Errorf("want %08x, got %08x", want, got)
+	}
+}
diff --git a/schedstat_test.go b/schedstat_test.go
index 42e43f1..c6bee61 100644
--- a/schedstat_test.go
+++ b/schedstat_test.go
@@ -13,7 +13,9 @@
 
 package procfs
 
-import "testing"
+import (
+	"testing"
+)
 
 func TestSchedstat(t *testing.T) {
 	stats, err := getProcFixtures(t).Schedstat()
@@ -36,18 +38,12 @@ func TestSchedstat(t *testing.T) {
 		t.Error("could not find cpu0")
 	}
 
-	if want, have := uint64(2045936778163039), cpu.RunningJiffies; want != have {
-		t.Errorf("want RunningJiffies %v, have %v", want, have)
-	}
-	if want, have := float64(2045936778163039)/userHZ, cpu.RunningSeconds(); want != have {
-		t.Errorf("want RunningSeconds() %v, have %v", want, have)
+	if want, have := uint64(2045936778163039), cpu.RunningNanoseconds; want != have {
+		t.Errorf("want RunningNanoseconds %v, have %v", want, have)
 	}
 
-	if want, have := uint64(343796328169361), cpu.WaitingJiffies; want != have {
-		t.Errorf("want WaitingJiffies %v, have %v", want, have)
-	}
-	if want, have := float64(343796328169361)/userHZ, cpu.WaitingSeconds(); want != have {
-		t.Errorf("want WaitingSeconds() %v, have %v", want, have)
+	if want, have := uint64(343796328169361), cpu.WaitingNanoseconds; want != have {
+		t.Errorf("want WaitingNanoseconds %v, have %v", want, have)
 	}
 
 	if want, have := uint64(4767485306), cpu.RunTimeslices; want != have {
@@ -66,18 +62,12 @@ func TestProcSchedstat(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	if want, have := uint64(411605849), schedstat.RunningJiffies; want != have {
-		t.Errorf("want RunningJiffies %v, have %v", want, have)
-	}
-	if want, have := float64(411605849)/userHZ, schedstat.RunningSeconds(); want != have {
-		t.Errorf("want RunningSeconds() %v, have %v", want, have)
+	if want, have := uint64(411605849), schedstat.RunningNanoseconds; want != have {
+		t.Errorf("want RunningNanoseconds %v, have %v", want, have)
 	}
 
-	if want, have := uint64(93680043), schedstat.WaitingJiffies; want != have {
-		t.Errorf("want WaitingJiffies %v, have %v", want, have)
-	}
-	if want, have := float64(93680043)/userHZ, schedstat.WaitingSeconds(); want != have {
-		t.Errorf("want WaitingSeconds() %v, have %v", want, have)
+	if want, have := uint64(93680043), schedstat.WaitingNanoseconds; want != have {
+		t.Errorf("want WaitingNanoseconds %v, have %v", want, have)
 	}
 
 	if want, have := uint64(79), schedstat.RunTimeslices; want != have {
@@ -113,11 +103,11 @@ func TestProcSchedstatMultipleLines(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	if want, have := uint64(123), schedstat.RunningJiffies; want != have {
-		t.Errorf("want RunningJiffies %v, have %v", want, have)
+	if want, have := uint64(123), schedstat.RunningNanoseconds; want != have {
+		t.Errorf("want RunningNanoseconds %v, have %v", want, have)
 	}
-	if want, have := uint64(456), schedstat.WaitingJiffies; want != have {
-		t.Errorf("want WaitingJiffies %v, have %v", want, have)
+	if want, have := uint64(456), schedstat.WaitingNanoseconds; want != have {
+		t.Errorf("want WaitingNanoseconds %v, have %v", want, have)
 	}
 	if want, have := uint64(789), schedstat.RunTimeslices; want != have {
 		t.Errorf("want RunTimeslices %v, have %v", want, have)
diff --git a/sysfs/class_cooling_device_test.go b/sysfs/class_cooling_device_test.go
new file mode 100644
index 0000000..a94a12c
--- /dev/null
+++ b/sysfs/class_cooling_device_test.go
@@ -0,0 +1,52 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build !windows
+
+package sysfs
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestClassCoolingDeviceStats(t *testing.T) {
+	fs, err := NewFS(sysTestFixtures)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	coolingDeviceTest, err := fs.ClassCoolingDeviceStats()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	classCoolingDeviceStats := []ClassCoolingDeviceStats{
+		{
+			Name:     "0",
+			Type:     "Processor",
+			MaxState: 50,
+			CurState: 0,
+		},
+		{
+			Name:     "1",
+			Type:     "intel_powerclamp",
+			MaxState: 27,
+			CurState: -1,
+		},
+	}
+
+	if !reflect.DeepEqual(classCoolingDeviceStats, coolingDeviceTest) {
+		t.Errorf("Result not correct: want %v, have %v", classCoolingDeviceStats, coolingDeviceTest)
+	}
+}
diff --git a/vm_test.go b/vm_test.go
index 492f166..b4d9aad 100644
--- a/vm_test.go
+++ b/vm_test.go
@@ -14,51 +14,72 @@
 package procfs
 
 import (
-	"reflect"
 	"testing"
+
+	"github.com/google/go-cmp/cmp"
 )
 
+func newPInt64(i int64) *int64 {
+	return &i
+}
+
 func TestVM(t *testing.T) {
 	fs, err := NewFS(procTestFixtures)
 	if err != nil {
 		t.Fatal(err)
 	}
-	stats, err := fs.VM()
+	got, err := fs.VM()
 	if err != nil {
 		t.Fatal(err)
 	}
-
-	lowmemreserveratio := []int64{256, 256, 32, 0, 0}
-	vm := &VM{
-		AdminReserveKbytes:        8192,
-		CompactUnevictableAllowed: 1,
-		DirtyBackgroundRatio:      10,
-		DirtyExpireCentisecs:      3000,
-		DirtyRatio:                20,
-		DirtytimeExpireSeconds:    43200,
-		DirtyWritebackCentisecs:   500,
-		ExtfragThreshold:          500,
-		LaptopMode:                5,
+	zeroPointer := newPInt64(0)
+	lowmemreserveratio := []*int64{newPInt64(256), newPInt64(256), newPInt64(32), zeroPointer, zeroPointer}
+	want := &VM{
+		AdminReserveKbytes:        newPInt64(8192),
+		BlockDump:                 zeroPointer,
+		CompactUnevictableAllowed: newPInt64(1),
+		DirtyBackgroundBytes:      zeroPointer,
+		DirtyBackgroundRatio:      newPInt64(10),
+		DirtyBytes:                zeroPointer,
+		DirtyExpireCentisecs:      newPInt64(3000),
+		DirtyRatio:                newPInt64(20),
+		DirtytimeExpireSeconds:    newPInt64(43200),
+		DirtyWritebackCentisecs:   newPInt64(500),
+		DropCaches:                zeroPointer,
+		ExtfragThreshold:          newPInt64(500),
+		HugetlbShmGroup:           zeroPointer,
+		LaptopMode:                newPInt64(5),
+		LegacyVaLayout:            zeroPointer,
 		LowmemReserveRatio:        lowmemreserveratio,
-		MaxMapCount:               65530,
-		MemoryFailureRecovery:     1,
-		MinFreeKbytes:             67584,
-		MinSlabRatio:              5,
-		MinUnmappedRatio:          1,
-		MmapMinAddr:               65536,
-		NumaStat:                  1,
+		MaxMapCount:               newPInt64(65530),
+		MemoryFailureEarlyKill:    zeroPointer,
+		MemoryFailureRecovery:     newPInt64(1),
+		MinFreeKbytes:             newPInt64(67584),
+		MinSlabRatio:              newPInt64(5),
+		MinUnmappedRatio:          newPInt64(1),
+		MmapMinAddr:               newPInt64(65536),
+		NumaStat:                  newPInt64(1),
 		NumaZonelistOrder:         "Node",
-		OomDumpTasks:              1,
-		OvercommitRatio:           50,
-		PageCluster:               3,
-		StatInterval:              1,
-		Swappiness:                60,
-		UserReserveKbytes:         131072,
-		VfsCachePressure:          100,
-		WatermarkBoostFactor:      15000,
-		WatermarkScaleFactor:      10,
+		NrHugepages:               zeroPointer,
+		NrHugepagesMempolicy:      zeroPointer,
+		NrOvercommitHugepages:     zeroPointer,
+		OomDumpTasks:              newPInt64(1),
+		OomKillAllocatingTask:     zeroPointer,
+		OvercommitKbytes:          zeroPointer,
+		OvercommitMemory:          zeroPointer,
+		OvercommitRatio:           newPInt64(50),
+		PageCluster:               newPInt64(3),
+		PanicOnOom:                zeroPointer,
+		PercpuPagelistFraction:    zeroPointer,
+		StatInterval:              newPInt64(1),
+		Swappiness:                newPInt64(60),
+		UserReserveKbytes:         newPInt64(131072),
+		VfsCachePressure:          newPInt64(100),
+		WatermarkBoostFactor:      newPInt64(15000),
+		WatermarkScaleFactor:      newPInt64(10),
+		ZoneReclaimMode:           zeroPointer,
 	}
-	if !reflect.DeepEqual(vm, stats) {
-		t.Errorf("Result not correct: want %v\n, have %v", vm, stats)
+	if diff := cmp.Diff(want, got); diff != "" {
+		t.Fatalf("unexpected power supply class (-want +got):\n%s", diff)
 	}
 }
diff --git a/xfs/parse_test.go b/xfs/parse_test.go
index fcbe9ef..1774665 100644
--- a/xfs/parse_test.go
+++ b/xfs/parse_test.go
@@ -311,6 +311,191 @@ func TestParseStats(t *testing.T) {
 				},
 			},
 		},
+		{
+			name:    "xstrat bad",
+			s:       "xstrat 1",
+			invalid: true,
+		},
+		{
+			name: "xstrat OK",
+			s:    "xstrat 1 2",
+			stats: &xfs.Stats{
+				Xstrat: xfs.XstratStats{
+					Quick: 1,
+					Split: 2,
+				},
+			},
+		},
+		{
+			name:    "push_ail bad",
+			s:       "push_ail 1 2 3 4 5",
+			invalid: true,
+		},
+		{
+			name: "push_ail OK",
+			s:    "push_ail 1 2 3 4 5 6 7 8 9 10",
+			stats: &xfs.Stats{
+				PushAil: xfs.PushAilStats{
+					TryLogspace:   1,
+					SleepLogspace: 2,
+					Pushes:        3,
+					Success:       4,
+					PushBuf:       5,
+					Pinned:        6,
+					Locked:        7,
+					Flushing:      8,
+					Restarts:      9,
+					Flush:         10,
+				},
+			},
+		},
+		{
+			name:    "debug bad",
+			s:       "debug 1 2",
+			invalid: true,
+		},
+		{
+			name: "debug  OK",
+			s:    "debug 1",
+			stats: &xfs.Stats{
+				Debug: xfs.DebugStats{
+					Enabled: 1,
+				},
+			},
+		},
+		{
+			name:    "qm bad",
+			s:       "qm 1 2 3 4 5 6 7",
+			invalid: true,
+		},
+		{
+			name: "qm OK",
+			s:    "qm 1 2 3 4 5 6 7 8",
+			stats: &xfs.Stats{
+				QuotaManager: xfs.QuotaManagerStats{
+					Reclaims:      1,
+					ReclaimMisses: 2,
+					DquoteDups:    3,
+					CacheMisses:   4,
+					CacheHits:     5,
+					Wants:         6,
+					ShakeReclaims: 7,
+					InactReclaims: 8,
+				},
+			},
+		},
+		{
+			name:    "abtb2 bad",
+			s:       "abtb2 1 2 3 4 5 6",
+			invalid: true,
+		},
+		{
+			name: "abtb2 OK",
+			s:    "abtb2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15",
+			stats: &xfs.Stats{
+				BtreeAllocBlocks2: xfs.BtreeAllocBlocks2Stats{
+					Lookup:    1,
+					Compare:   2,
+					Insrec:    3,
+					Delrec:    4,
+					NewRoot:   5,
+					KillRoot:  6,
+					Increment: 7,
+					Decrement: 8,
+					Lshift:    9,
+					Rshift:    10,
+					Split:     11,
+					Join:      12,
+					Alloc:     13,
+					Free:      14,
+					Moves:     15,
+				},
+			},
+		},
+		{
+			name:    "abtc2 bad",
+			s:       "abtc2 1 2 3 4 5 6",
+			invalid: true,
+		},
+		{
+			name: "abtc2 OK",
+			s:    "abtc2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15",
+			stats: &xfs.Stats{
+				BtreeAllocContig2: xfs.BtreeAllocContig2Stats{
+					Lookup:    1,
+					Compare:   2,
+					Insrec:    3,
+					Delrec:    4,
+					NewRoot:   5,
+					KillRoot:  6,
+					Increment: 7,
+					Decrement: 8,
+					Lshift:    9,
+					Rshift:    10,
+					Split:     11,
+					Join:      12,
+					Alloc:     13,
+					Free:      14,
+					Moves:     15,
+				},
+			},
+		},
+		{
+			name:    "bmbt2 bad",
+			s:       "bmbt2 1 2 3 4 5 6",
+			invalid: true,
+		},
+		{
+			name: "bmbt2 OK",
+			s:    "bmbt2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15",
+			stats: &xfs.Stats{
+				BtreeBlockMap2: xfs.BtreeBlockMap2Stats{
+					Lookup:    1,
+					Compare:   2,
+					Insrec:    3,
+					Delrec:    4,
+					NewRoot:   5,
+					KillRoot:  6,
+					Increment: 7,
+					Decrement: 8,
+					Lshift:    9,
+					Rshift:    10,
+					Split:     11,
+					Join:      12,
+					Alloc:     13,
+					Free:      14,
+					Moves:     15,
+				},
+			},
+		},
+		{
+			name:    "ibt2 bad",
+			s:       "ibt2 1 2 3 4 5 6",
+			invalid: true,
+		},
+		{
+			name: "ibt2 OK",
+			s:    "ibt2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15",
+			stats: &xfs.Stats{
+				BtreeInode2: xfs.BtreeInode2Stats{
+					Lookup:    1,
+					Compare:   2,
+					Insrec:    3,
+					Delrec:    4,
+					NewRoot:   5,
+					KillRoot:  6,
+					Increment: 7,
+					Decrement: 8,
+					Lshift:    9,
+					Rshift:    10,
+					Split:     11,
+					Join:      12,
+					Alloc:     13,
+					Free:      14,
+					Moves:     15,
+				},
+			},
+		},
 		{
 			name: "fixtures OK",
 			fs:   true,
@@ -410,6 +595,106 @@ func TestParseStats(t *testing.T) {
 					WriteBytes: 92823103,
 					ReadBytes:  86219234,
 				},
+				PushAil: xfs.PushAilStats{
+					TryLogspace:   945014,
+					SleepLogspace: 0,
+					Pushes:        134260,
+					Success:       15483,
+					PushBuf:       0,
+					Pinned:        3940,
+					Locked:        464,
+					Flushing:      159985,
+					Restarts:      0,
+					Flush:         40,
+				},
+				Xstrat: xfs.XstratStats{
+					Quick: 92447,
+					Split: 0,
+				},
+				Debug: xfs.DebugStats{
+					Enabled: 0,
+				},
+				QuotaManager: xfs.QuotaManagerStats{
+					Reclaims:      0,
+					ReclaimMisses: 0,
+					DquoteDups:    0,
+					CacheMisses:   0,
+					CacheHits:     0,
+					Wants:         0,
+					ShakeReclaims: 0,
+					InactReclaims: 0,
+				},
+				BtreeAllocBlocks2: xfs.BtreeAllocBlocks2Stats{
+					Lookup:    184941,
+					Compare:   1277345,
+					Insrec:    13257,
+					Delrec:    13278,
+					NewRoot:   0,
+					KillRoot:  0,
+					Increment: 0,
+					Decrement: 0,
+					Lshift:    0,
+					Rshift:    0,
+					Split:     0,
+					Join:      0,
+					Alloc:     0,
+					Free:      0,
+					Moves:     2746147,
+				},
+
+				BtreeAllocContig2: xfs.BtreeAllocContig2Stats{
+					Lookup:    345295,
+					Compare:   2416764,
+					Insrec:    172637,
+					Delrec:    172658,
+					NewRoot:   0,
+					KillRoot:  0,
+					Increment: 0,
+					Decrement: 0,
+					Lshift:    0,
+					Rshift:    0,
+					Split:     0,
+					Join:      0,
+					Alloc:     0,
+					Free:      0,
+					Moves:     21406023,
+				},
+
+				BtreeBlockMap2: xfs.BtreeBlockMap2Stats{
+					Lookup:    0,
+					Compare:   0,
+					Insrec:    0,
+					Delrec:    0,
+					NewRoot:   0,
+					KillRoot:  0,
+					Increment: 0,
+					Decrement: 0,
+					Lshift:    0,
+					Rshift:    0,
+					Split:     0,
+					Join:      0,
+					Alloc:     0,
+					Free:      0,
+					Moves:     0,
+				},
+
+				BtreeInode2: xfs.BtreeInode2Stats{
+					Lookup:    343004,
+					Compare:   1358467,
+					Insrec:    0,
+					Delrec:    0,
+					NewRoot:   0,
+					KillRoot:  0,
+					Increment: 0,
+					Decrement: 0,
+					Lshift:    0,
+					Rshift:    0,
+					Split:     0,
+					Join:      0,
+					Alloc:     0,
+					Free:      0,
+					Moves:     0,
+				},
 			},
 		},
 	}
diff --git a/zoneinfo_test.go b/zoneinfo_test.go
new file mode 100644
index 0000000..87b0ab1
--- /dev/null
+++ b/zoneinfo_test.go
@@ -0,0 +1,44 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build !windows
+
+package procfs
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+func TestZoneinfo(t *testing.T) {
+	fs := getProcFixtures(t)
+	protection1 := []*int64{newPInt64(0), newPInt64(2877), newPInt64(7826), newPInt64(7826), newPInt64(7826)}
+	protection2 := []*int64{newPInt64(0), newPInt64(0), newPInt64(4949), newPInt64(4949), newPInt64(4949)}
+	refs := []Zoneinfo{
+		{Node: "0", Zone: "", NrFreePages: newPInt64(3952), Min: newPInt64(33), Low: newPInt64(41), High: newPInt64(49), Spanned: newPInt64(4095), Present: newPInt64(3975), Managed: newPInt64(3956), NrActiveAnon: newPInt64(547580), NrInactiveAnon: newPInt64(230981), NrIsolatedAnon: newPInt64(0), NrAnonPages: newPInt64(795576), NrAnonTransparentHugepages: newPInt64(0), NrActiveFile: newPInt64(346282), NrInactiveFile: newPInt64(316904), NrIsolatedFile: newPInt64(0), NrFilePages: newPInt64(761874), NrSlabReclaimable: newPInt64(131220), NrSlabUnreclaimable: newPInt64(47320), NrKernelStack: newPInt64(0), NrMapped: newPInt64(215483), NrDirty: newPInt64(908), NrWriteback: newPInt64(0), NrUnevictable: newPInt64(115467), NrShmem: newPInt64(224925), NrDirtied: newPInt64(8007423), NrWritten: newPInt64(7752121), NumaHit: newPInt64(1), NumaMiss: newPInt64(0), NumaForeign: newPInt64(0), NumaInterleave: newPInt64(0), NumaLocal: newPInt64(1), NumaOther: newPInt64(0), Protection: protection1},
+		{Node: "0", Zone: "DMA32", NrFreePages: newPInt64(204252), Min: newPInt64(19510), Low: newPInt64(21059), High: newPInt64(22608), Spanned: newPInt64(1044480), Present: newPInt64(759231), Managed: newPInt64(742806), NrKernelStack: newPInt64(2208), NumaHit: newPInt64(113952967), NumaMiss: newPInt64(0), NumaForeign: newPInt64(0), NumaInterleave: newPInt64(0), NumaLocal: newPInt64(113952967), NumaOther: newPInt64(0), Protection: protection2},
+	}
+	data, err := fs.Zoneinfo()
+	if err != nil {
+		t.Fatalf("failed to parse zoneinfo: %v", err)
+	}
+
+	for index, ref := range refs {
+		want, got := ref, data[index]
+		if diff := cmp.Diff(want, got); diff != "" {
+			t.Fatalf("unexpected crypto entry (-want +got):\n%s", diff)
+		}
+
+	}
+}