#!/usr/bin/python # # Tool to set up Linux large page support with minimal effort # # by Jarod Wilson # (c) Red Hat, Inc., 2009 # import os debug = False # config files we need access to sysctlConf = "/etc/sysctl.conf" if not os.access(sysctlConf, os.W_OK): print "Cannot access %s" % sysctlConf if debug == False: os._exit(1) # This file will be created if it doesn't exist limitsConf = "/etc/security/limits.d/hugepages.conf" # Figure out what we've got in the way of memory memInfo = open("/proc/meminfo").readlines() memTotal = 0 hugePages = 0 hugePageSize = 0 for line in memInfo: if line.startswith("MemTotal:"): memTotal = int(line.split()[1]) break for line in memInfo: if line.startswith("HugePages_Total:"): hugePages = int(line.split()[1]) break for line in memInfo: if line.startswith("Hugepagesize:"): hugePageSize = int(line.split()[1]) break if hugePageSize == 0: print "Aborting, cannot determine system huge page size!" os._exit(1) # Get initial sysctl settings shmmax = 0 nr_hugepages = 0 hugeGID = 0 sysctlCur = os.popen("/sbin/sysctl -a").readlines() for line in sysctlCur: if line.startswith("kernel.shmmax = "): shmmax = int(line.split()[2]) break for line in sysctlCur: if line.startswith("vm.nr_hugepages = "): nr_hugepages = int(line.split()[2]) break for line in sysctlCur: if line.startswith("vm.hugetlb_shm_group = "): hugeGID = int(line.split()[2]) break # translate group into textual version hugeGIDName = "null" groupNames = os.popen("/usr/bin/getent group").readlines() for line in groupNames: curGID = int(line.split(":")[2]) if curGID == hugeGID: hugeGIDName = line.split(":")[0] break # dump system config as we see it before we start tweaking it print "Current configuration:" print " * Total System Memory......: %6d MB" % (memTotal / 1024) print " * Shared Mem Max Mapping...: %6d MB" % (shmmax / (1024 * 1024)) print " * System Huge Page Size....: %6d MB" % (hugePageSize / 1024) print " * Number of Huge Pages.....: %6d" % hugePages print " * Total size of Huge Pages.: %6d MB" % (hugePages * hugePageSize / 1024) print " * Remaining System Memory..: %6d MB" % ((memTotal / 1024) - (hugePages * hugePageSize / 1024)) print " * Huge Page User Group.....: %s (%d)" % (hugeGIDName, hugeGID) print # determine some sanity safeguards halfOfMem = memTotal / 2 allMemLess2G = memTotal - 2048000 if halfOfMem >= allMemLess2G: maxHugePageReqKB = halfOfMem else: maxHugePageReqKB = allMemLess2G maxHugePageReqMB = maxHugePageReqKB / 1024 maxHugePageReq = maxHugePageReqKB / hugePageSize # ask how memory they want to allocate for huge pages userIn = None while not userIn: try: userIn = raw_input("How much memory would you like to allocate for huge pages? " "(input in MB, unless postfixed with GB): ") if userIn[-2:] == "GB": userHugePageReqMB = int(userIn[0:-2]) * 1024 elif userIn[-1:] == "G": userHugePageReqMB = int(userIn[0:-1]) * 1024 elif userIn[-2:] == "MB": userHugePageReqMB = int(userIn[0:-2]) elif userIn[-1:] == "M": userHugePageReqMB = int(userIn[0:-1]) else: userHugePageReqMB = int(userIn) if userHugePageReqMB > maxHugePageReqMB: userIn = None print "Sorry, the most I'll let you allocate is %d MB, try again!" % maxHugePageReqMB elif userHugePageReqMB < (hugePageSize / 1024): userIn = None print "Sorry, allocation must be at least a page's worth!" else: break except ValueError: userIn = None print "Input must be an integer, please try again!" userHugePageReqKB = userHugePageReqMB * 1024 userHugePagesReq = userHugePageReqKB / hugePageSize print "Okay, we'll try to allocate %d MB for huge pages..." % userHugePageReqMB print # some basic user input validation badchars = list(' \\\'":;~`!$^&*(){}[]?/><,') inputIsValid = False # ask for the name of the group allowed access to huge pages while inputIsValid == False: foundbad = False userGroupReq = raw_input("What group should have access to the huge pages? " "(The group will be created, if need be): ") if not userGroupReq: foundbad = True print "You must input a group name, please try again!" elif userGroupReq[0].isdigit() or userGroupReq[0] == "-": foundbad = True print "Group names cannot start with a number or dash, please try again!" for char in badchars: if char in userGroupReq: foundbad = True print "Illegal characters in group name, please try again!" break if len(userGroupReq) > 16: foundbad = True print "Group names can't be more than 16 characaters, please try again!" if foundbad == False: inputIsValid = True print "Okay, we'll give group %s access to the huge pages" % userGroupReq # see if group already exists, use it if it does, if not, create it userGIDReq = -1 for line in groupNames: curGroupName = line.split(":")[0] if curGroupName == userGroupReq: userGIDReq = int(line.split(":")[2]) break if userGIDReq > -1: print "Group %s (gid %d) already exists, we'll use it" % (userGroupReq, userGIDReq) else: if debug == False: os.popen("/usr/sbin/groupadd %s" % userGroupReq) else: print "/usr/sbin/groupadd %s" % userGroupReq groupNames = os.popen("/usr/bin/getent group %s" % userGroupReq).readlines() for line in groupNames: curGroupName = line.split(":")[0] if curGroupName == userGroupReq: userGIDReq = int(line.split(":")[2]) break print "Created group %s (gid %d) for huge page use" % (userGroupReq, userGIDReq) print # basic user input validation, take 2 # space is valid in this case, wasn't in the prior incarnation badchars = list('\\\'":;~`!$^&*(){}[]?/><,') inputIsValid = False # ask for user(s) that should be in the huge page access group while inputIsValid == False: foundbad = False userUsersReq = raw_input("What user(s) should have access to the huge pages (space-delimited list, users created as needed)? ") for char in badchars: if char in userUsersReq: foundbad = True print "Illegal characters in user name(s) or invalid list format, please try again!" break for n in userUsersReq.split(): if len(n) > 32: foundbad = True print "User names can't be more than 32 characaters, please try again!" break if n[0] == "-": foundbad = True print "User names cannot start with a dash, please try again!" break if foundbad == False: inputIsValid = True # see if user(s) already exist(s) curUserList = os.popen("/usr/bin/getent passwd").readlines() hugePageUserList = userUsersReq.split() for hugeUser in hugePageUserList: userExists = False for line in curUserList: curUser = line.split(":")[0] if curUser == hugeUser: print "Adding user %s to huge page group" % hugeUser userExists = True if debug == False: os.popen("/usr/sbin/usermod -a -G %s %s" % (userGroupReq, hugeUser)) else: print "/usr/sbin/usermod -a -G %s %s" % (userGroupReq, hugeUser) if userExists == True: break if userExists == False: print "Creating user %s with membership in huge page group" % hugeUser if debug == False: os.popen("/usr/sbin/useradd %s -G %s" % (hugeUser, userGroupReq)) else: print "/usr/sbin/useradd %s -G %s" % (hugeUser, userGroupReq) print # set sysctl values for the current running environment if debug == False: os.popen("/sbin/sysctl -w kernel.shmmax=%d" % (userHugePageReqMB * 1024 * 1024)) os.popen("/sbin/sysctl -w vm.nr_hugepages=%d" % userHugePagesReq) os.popen("/sbin/sysctl -w vm.hugetlb_shm_group=%d" % userGIDReq) else: print "/sbin/sysctl -w kernel.shmmax=%d" % (userHugePageReqMB * 1024 * 1024) print "/sbin/sysctl -w vm.nr_hugepages=%d" % userHugePagesReq print "/sbin/sysctl -w vm.hugetlb_shm_group=%d" % userGIDReq print # write out sysctl config changes to persist across reboot if debug == False: sysctlConfLines = "# sysctl configuration\n" if os.access(sysctlConf, os.W_OK): try: sysctlConfLines = open(sysctlConf).readlines() os.rename(sysctlConf, sysctlConf + ".backup") print("Saved original %s as %s.backup" % (sysctlConf, sysctlConf)) except: pass fd = open(sysctlConf, "w") for line in sysctlConfLines: if line.startswith("kernel.shmmax"): continue elif line.startswith("vm.nr_hugepages"): continue elif line.startswith("vm.hugetlb_shm_group"): continue else: fd.write(line); fd.write("kernel.shmmax = %d\n" % (userHugePageReqMB * 1024 * 1024)) fd.write("vm.nr_hugepages = %d\n" % userHugePagesReq) fd.write("vm.hugetlb_shm_group = %d\n" % userGIDReq) fd.close() else: print "Add to %s:" % sysctlConf print "kernel.shmmax = %d" % (userHugePageReqMB * 1024 * 1024) print "vm.nr_hugepages = %d" % userHugePagesReq print "vm.hugetlb_shm_group = %d" % userGIDReq print # write out limits.conf changes to persist across reboot if debug == False: limitsConfLines = "# Huge page access configuration\n" if os.access(limitsConf, os.W_OK): try: limitsConfLines = open(limitsConf).readlines() os.rename(limitsConf, limitsConf + ".backup") print("Saved original %s as %s.backup" % (limitsConf, limitsConf)) except: pass fd = open(limitsConf, "w") for line in limitsConfLines: cfgExist = False for hugeUser in hugePageUserList: if line.split()[0] == hugeUser: cfgExist = True if cfgExist == True: continue else: fd.write(line) for hugeUser in hugePageUserList: fd.write("%s soft memlock %d\n" % (hugeUser, userHugePageReqKB)) fd.write("%s hard memlock %d\n" % (hugeUser, userHugePageReqKB)) fd.close() else: print "Add to %s:" % limitsConf for hugeUser in hugePageUserList: print "%s soft memlock %d" % (hugeUser, userHugePageReqKB) print "%s hard memlock %d" % (hugeUser, userHugePageReqKB) # dump the final configuration of things now that we're done tweaking print print "Final configuration:" print " * Total System Memory......: %6d MB" % (memTotal / 1024) print " * Shared Mem Max Mapping...: %6d MB" % userHugePageReqMB print " * System Huge Page Size....: %6d MB" % (hugePageSize / 1024) print " * Available Huge Pages.....: %6d" % userHugePagesReq print " * Total size of Huge Pages.: %6d MB" % userHugePageReqMB print " * Remaining System Memory..: %6d MB" % ((memTotal / 1024) - userHugePageReqMB) print " * Huge Page User Group.....: %s (%d)" % (userGroupReq, userGIDReq) print