DPDK patches and discussions
 help / color / mirror / Atom feed
From: Anatoly Burakov <anatoly.burakov@intel.com>
To: dev@dpdk.org
Cc: john.mcnamara@intel.com, bruce.richardson@intel.com,
	pablo.de.lara.guarch@intel.com, david.hunt@intel.com,
	mohammad.abdul.awal@intel.com, thomas@monjalon.net,
	ferruh.yigit@intel.com
Subject: [dpdk-dev] [RFC v2 9/9] usertools/lib: add GRUB utility library for hugepage config
Date: Thu, 15 Nov 2018 15:47:21 +0000	[thread overview]
Message-ID: <58e36e0a9bc7f9cb3d5a288feed1230984e72ee6.1542291869.git.anatoly.burakov@intel.com> (raw)
In-Reply-To: <cover.1542291869.git.anatoly.burakov@intel.com>
In-Reply-To: <cover.1542291869.git.anatoly.burakov@intel.com>

This library is highly experimental and can kill kittens, but its
main purpose is to automatically set up GRUB command-line to
allocate a given number of hugepages at boot time. It works in
a similar way HugeUtil library does, but instead of committing
changes to fstab or runtime configuration, it commits its
changes to GRUB default command-line and updates all GRUB entries
afterwards. I got it to a state where it's safe to use on my
system, but see the part above about killing kittens - you have
been warned :)

No example scripts will currently be provided.

Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 usertools/DPDKConfigLib/GrubHugeUtil.py | 175 ++++++++++++++++++++++++
 1 file changed, 175 insertions(+)
 create mode 100755 usertools/DPDKConfigLib/GrubHugeUtil.py

diff --git a/usertools/DPDKConfigLib/GrubHugeUtil.py b/usertools/DPDKConfigLib/GrubHugeUtil.py
new file mode 100755
index 000000000..4b8e349b8
--- /dev/null
+++ b/usertools/DPDKConfigLib/GrubHugeUtil.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Intel Corporation
+
+
+from .PlatformInfo import *
+from .Util import *
+import re
+import os
+
+__KERNEL_TRANSPARENT_HP = "/sys/kernel/mm/transparent_hugepage/enabled"
+_GRUB_CMDLINE_PARAM_NAME = "GRUB_CMDLINE_LINUX_DEFAULT"
+
+# local copy of platform info
+info = PlatformInfo()
+
+def _find_linux_default_cmdline():
+    with open("/etc/default/grub") as f:
+        for line in f:
+            line = line.strip()
+            if line.startswith(_GRUB_CMDLINE_PARAM_NAME):
+                return line
+        else:
+            raise RuntimeError("Invalid GRUB default configuration format")
+
+
+def _parse_linux_default_cmdline(line):
+    # get value to the right of equals sign, strip whitespace and quotes,
+    # split into separate keys and make a list of values
+    _, cmdline = kv_split(line, "=")
+    # remove quotes
+    if cmdline[0] == cmdline[-1] == '"':
+        cmdline = cmdline[1:-1]
+
+    return [kv_split(v, "=") for v in cmdline.split()]
+
+
+def _generate_linux_default_cmdline(cmdline):
+    lines = []
+    cmdline_idx = -1
+    with open("/etc/default/grub") as f:
+        for idx, line in enumerate(f):
+            line = line.strip()
+            lines.extend([line])
+            if line.startswith(_GRUB_CMDLINE_PARAM_NAME):
+                cmdline_idx = idx
+        if cmdline_idx == -1:
+            raise RuntimeError("Invalid GRUB default configuration format")
+
+    # write the lines back, replacing one we want
+    with open("/etc/default/grub", "w") as f:
+        for idx, line in enumerate(lines):
+            if idx == cmdline_idx:
+                line = cmdline
+            f.write(line + "\n")
+
+
+def _find_transparent_hugepage():
+    if not os.path.exists(__KERNEL_TRANSPARENT_HP):
+        return None
+    value = read_file(__KERNEL_TRANSPARENT_HP)
+    m = re.search(r"\[([a-z]+)\]", value)
+    if not m:
+        raise RuntimeError("BUG: Bad regular expression")
+    return m.group(1)
+
+
+class GrubHugepageConfig:
+    def __init__(self):
+        self.update()
+
+    def update(self):
+        self.reset()
+
+        hugepage_sizes = info.hugepage_sizes_supported
+        if len(hugepage_sizes) == 0:
+            raise RuntimeError("Hugepages appear to be unsupported")
+        cmdline = _find_linux_default_cmdline()
+        values = _parse_linux_default_cmdline(cmdline)
+
+        # parse values in the list
+        self.default_hugepagesz = info.default_hugepage_size
+        self.transparent_hugepage = _find_transparent_hugepage()
+        sizes = []
+        nrs = []
+        for k, v in values:
+            if k == "default_hugepagesz":
+                self.default_hugepagesz = human_readable_to_kilobytes(v)
+            elif k == "transparent_hugepage":
+                self.transparent_hugepage = v
+            elif k == "hugepagesz":
+                sizes.append(human_readable_to_kilobytes(v))
+            elif k == "hugepages":
+                nrs.append(v)
+        if len(sizes) != len(nrs):
+            raise RuntimeError("GRUB hugepage configuration is wrong")
+        detected_hugepages = dict(zip(sizes, map(int, nrs)))
+        self.nr_hugepages = {size: detected_hugepages.get(size, 0)
+                             for size in hugepage_sizes}
+
+    def commit(self):
+        # perform sanity checks - we can't afford invalid data making it into
+        # bootloader config, as that might render user's machine unbootable, so
+        # tread really really carefully
+
+        # first, check if user didn't add any unexpected hugepage sizes
+        configured_sizes = set(self.nr_hugepages.keys())
+        supported_sizes = set(info.hugepage_sizes_supported)
+
+        if configured_sizes != supported_sizes:
+            diff = configured_sizes.difference(supported_sizes)
+            raise ValueError("Unsupported hugepage sizes: %s" %
+                             [kilobytes_to_human_readable(s) for s in diff])
+
+        # check if default hugepage is one of the supported ones
+        if self.default_hugepagesz is not None and\
+                self.default_hugepagesz not in configured_sizes:
+            s = kilobytes_to_human_readable(self.default_hugepagesz)
+            raise ValueError("Unsupported default hugepage size: %i" % s)
+
+        # transparent hugepages support was added in recent kernels, so check
+        # if user is trying to set this
+        if _find_transparent_hugepage() is None and \
+                        self.transparent_hugepage is not None:
+            raise ValueError("Transparent hugepages are not unsupported")
+
+        # OK, parameters look to be valid - let's roll
+
+        # read and parse current cmdline
+        cmdline = _find_linux_default_cmdline()
+
+        values = _parse_linux_default_cmdline(cmdline)
+
+        # clear out old data
+        klist = ["transparent_hugepage", "default_hugepagesz",
+                 "hugepage", "hugepagesz"]
+        # iterate over a copy so that we could delete items
+        for k, v in values[:]:
+            if k in klist:
+                values.remove((k, v))
+
+        # generate new cmdline
+        cmdline = " ".join([("%s=%s" % (k, v)) if v is not None else k
+                            for k, v in values])
+
+        # now, populate cmdline with new data
+        new_items = []
+        for sz, nr in self.nr_hugepages.items():
+            sz = kilobytes_to_human_readable(sz)
+            new_items += "hugepagesz=%s hugepages=%i" % (sz, nr)
+        if self.default_hugepagesz is not None:
+            new_items += "default_hugepagesz=%i" % self.default_hugepagesz
+        if self.transparent_hugepage is not None:
+            new_items += "transparent_hugepage=%s" % self.transparent_hugepage
+
+        cmdline = "%s %s" % (cmdline, " ".join(new_items))
+
+        # strip any extraneous whitespace we may have added
+        cmdline = re.sub(r"\s\s+", " ", cmdline).strip()
+
+        # now, put everything back together
+        cmdline = '%s="%s"' % (_GRUB_CMDLINE_PARAM_NAME, cmdline)
+
+        # write it to config
+        _generate_linux_default_cmdline(cmdline)
+
+        # finally, update GRUB
+        if not run(["update-grub"]):
+            raise RuntimeError("Failed to update GRUB")
+        self.update()
+
+    def reset(self):
+        self.nr_hugepages = {}  # pagesz: number
+        self.default_hugepagesz = None
+        self.transparent_hugepage = None
-- 
2.17.1

      parent reply	other threads:[~2018-11-15 15:47 UTC|newest]

Thread overview: 36+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-06-25 15:59 [dpdk-dev] [RFC 0/9] Modularize and enhance DPDK Python scripts Anatoly Burakov
2018-06-25 15:59 ` [dpdk-dev] [RFC 1/9] usertools: add DPDK config lib python library Anatoly Burakov
2018-06-25 15:59 ` [dpdk-dev] [RFC 2/9] usertools/lib: add platform info library Anatoly Burakov
2018-06-25 15:59 ` [dpdk-dev] [RFC 3/9] usertools/cpu_layout: rewrite to use DPDKConfigLib Anatoly Burakov
2018-06-25 15:59 ` [dpdk-dev] [RFC 4/9] usertools/lib: support FreeBSD for platform info Anatoly Burakov
2018-06-25 15:59 ` [dpdk-dev] [RFC 5/9] usertools/lib: add device information library Anatoly Burakov
2018-06-25 15:59 ` [dpdk-dev] [RFC 6/9] usertools/devbind: switch to using DPDKConfigLib Anatoly Burakov
2018-06-25 15:59 ` [dpdk-dev] [RFC 7/9] usertools/lib: add hugepage information library Anatoly Burakov
2018-06-25 15:59 ` [dpdk-dev] [RFC 8/9] usertools: add hugepage info script Anatoly Burakov
2018-06-25 15:59 ` [dpdk-dev] [RFC 9/9] usertools/lib: add GRUB utility library for hugepage config Anatoly Burakov
2018-06-26  1:09   ` Kevin Wilson
2018-06-26  9:05     ` Burakov, Anatoly
2018-08-14 10:11 ` [dpdk-dev] [RFC 0/9] Modularize and enhance DPDK Python scripts Burakov, Anatoly
2018-08-28  8:16   ` Burakov, Anatoly
2018-11-15 15:47 ` [dpdk-dev] [RFC v2 " Anatoly Burakov
2018-11-15 15:47 ` [dpdk-dev] [RFC v2 1/9] usertools: add DPDK config lib python library Anatoly Burakov
2018-11-16  0:45   ` Stephen Hemminger
2018-11-16 11:49     ` Burakov, Anatoly
2018-11-16 14:09       ` Wiles, Keith
2018-11-16 14:13         ` Richardson, Bruce
2018-11-16 14:37           ` Burakov, Anatoly
2018-11-16 14:55             ` Thomas Monjalon
2018-11-16 15:41               ` Wiles, Keith
2018-11-16 15:43               ` Burakov, Anatoly
2018-11-16 15:58                 ` Thomas Monjalon
2018-11-16 16:10                   ` Bruce Richardson
2018-11-16 16:08                 ` Bruce Richardson
2018-11-16 15:38             ` Wiles, Keith
2018-11-15 15:47 ` [dpdk-dev] [RFC v2 2/9] usertools/lib: add platform info library Anatoly Burakov
2018-11-15 15:47 ` [dpdk-dev] [RFC v2 3/9] usertools/cpu_layout: rewrite to use DPDKConfigLib Anatoly Burakov
2018-11-15 15:47 ` [dpdk-dev] [RFC v2 4/9] usertools/lib: support FreeBSD for platform info Anatoly Burakov
2018-11-15 15:47 ` [dpdk-dev] [RFC v2 5/9] usertools/lib: add device information library Anatoly Burakov
2018-11-15 15:47 ` [dpdk-dev] [RFC v2 6/9] usertools/devbind: switch to using DPDKConfigLib Anatoly Burakov
2018-11-15 15:47 ` [dpdk-dev] [RFC v2 7/9] usertools/lib: add hugepage information library Anatoly Burakov
2018-11-15 15:47 ` [dpdk-dev] [RFC v2 8/9] usertools: add hugepage info script Anatoly Burakov
2018-11-15 15:47 ` Anatoly Burakov [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=58e36e0a9bc7f9cb3d5a288feed1230984e72ee6.1542291869.git.anatoly.burakov@intel.com \
    --to=anatoly.burakov@intel.com \
    --cc=bruce.richardson@intel.com \
    --cc=david.hunt@intel.com \
    --cc=dev@dpdk.org \
    --cc=ferruh.yigit@intel.com \
    --cc=john.mcnamara@intel.com \
    --cc=mohammad.abdul.awal@intel.com \
    --cc=pablo.de.lara.guarch@intel.com \
    --cc=thomas@monjalon.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).