commit d52a926f8f8eff50d6a9c6f8f39577185392c25b Author: ferreo Date: Mon Sep 2 21:09:26 2024 +0100 first commit diff --git a/.github/build-canary-v3 b/.github/build-canary-v3 new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/.github/build-canary-v3 @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/.github/build-nest-v3 b/.github/build-nest-v3 new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/.github/build-nest-v3 @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/.github/release-canary-v3 b/.github/release-canary-v3 new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/.github/release-canary-v3 @@ -0,0 +1 @@ +1 diff --git a/.github/release-nest-v3 b/.github/release-nest-v3 new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/.github/release-nest-v3 @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/.github/workflows/build-canaryv3.yml b/.github/workflows/build-canaryv3.yml new file mode 100644 index 0000000..7b9e211 --- /dev/null +++ b/.github/workflows/build-canaryv3.yml @@ -0,0 +1,40 @@ +name: PikaOS Package Build Only (Canary) (amd64-v3) + +on: + push: + branches: + - main + paths: + - '.github/build-canary-v3' + +jobs: + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/pikaos-linux/pikaos-builder:canaryv3 + volumes: + - /proc:/proc + options: --privileged -it + + steps: + - uses: actions/checkout@v3 + + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSH_KEY }} + name: id_rsa + known_hosts: ${{ secrets.KNOWN_HOSTS }} + if_key_exists: replace + + - name: Update APT Cache + run: apt-get update -y + + - name: Set Build Config + run: cp -vf ./pika-build-config/amd64-v3.sh ./pika-build-config.sh + + - name: Setup Makefile + run: cp -vf ./Makefile-v3 ./Makefile + + - name: Build Package + run: ./main.sh diff --git a/.github/workflows/build-nestv3.yml b/.github/workflows/build-nestv3.yml new file mode 100644 index 0000000..fd391dc --- /dev/null +++ b/.github/workflows/build-nestv3.yml @@ -0,0 +1,40 @@ +name: PikaOS Package Build Only (amd64-v3) + +on: + push: + branches: + - main + paths: + - '.github/build-nest-v3' + +jobs: + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/pikaos-linux/pikaos-builder:nestv3 + volumes: + - /proc:/proc + options: --privileged -it + + steps: + - uses: actions/checkout@v3 + + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSH_KEY }} + name: id_rsa + known_hosts: ${{ secrets.KNOWN_HOSTS }} + if_key_exists: replace + + - name: Update APT Cache + run: apt-get update -y + + - name: Set Build Config + run: cp -vf ./pika-build-config/amd64-v3.sh ./pika-build-config.sh + + - name: Setup Makefile + run: cp -vf ./Makefile-v3 ./Makefile + + - name: Build Package + run: ./main.sh diff --git a/.github/workflows/release-canaryv3.yml b/.github/workflows/release-canaryv3.yml new file mode 100644 index 0000000..d799864 --- /dev/null +++ b/.github/workflows/release-canaryv3.yml @@ -0,0 +1,43 @@ +name: PikaOS Package Build & Release (Canary) (amd64-v3) + +on: + push: + branches: + - main + paths: + - '.github/release-canary-v3' + +jobs: + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/pikaos-linux/pikaos-builder:canaryv3 + volumes: + - /proc:/proc + options: --privileged -it + + steps: + - uses: actions/checkout@v3 + + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSH_KEY }} + name: id_rsa + known_hosts: ${{ secrets.KNOWN_HOSTS }} + if_key_exists: replace + + - name: Update APT Cache + run: apt-get update -y + + - name: Set Build Config + run: cp -vf ./pika-build-config/amd64-v3.sh ./pika-build-config.sh + + - name: Setup Makefile + run: cp -vf ./Makefile-v3 ./Makefile + + - name: Build Package + run: ./main.sh + + - name: Release Package + run: ./release.sh diff --git a/.github/workflows/release-nestv3.yml b/.github/workflows/release-nestv3.yml new file mode 100644 index 0000000..8f21bd9 --- /dev/null +++ b/.github/workflows/release-nestv3.yml @@ -0,0 +1,43 @@ +name: PikaOS Package Build & Release (amd64-v3) + +on: + push: + branches: + - main + paths: + - '.github/release-nest-v3' + +jobs: + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/pikaos-linux/pikaos-builder:nestv3 + volumes: + - /proc:/proc + options: --privileged -it + + steps: + - uses: actions/checkout@v3 + + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSH_KEY }} + name: id_rsa + known_hosts: ${{ secrets.KNOWN_HOSTS }} + if_key_exists: replace + + - name: Update APT Cache + run: apt-get update -y + + - name: Set Build Config + run: cp -vf ./pika-build-config/amd64-v3.sh ./pika-build-config.sh + + - name: Setup Makefile + run: cp -vf ./Makefile-v3 ./Makefile + + - name: Build Package + run: ./main.sh + + - name: Release Package + run: ./release.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40e3e6b --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ +.idea + +falcon \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..125ed9b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 PikaOS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile-v3 b/Makefile-v3 new file mode 100644 index 0000000..5608ce4 --- /dev/null +++ b/Makefile-v3 @@ -0,0 +1,7 @@ +all: + true + +install: + mkdir -p $(DESTDIR)/usr/bin/ + GOAMD=v3 go build -ldflags="-s -w" -o $(DESTDIR)/usr/bin/falcon -buildvcs=false + chmod 755 $(DESTDIR)/usr/bin/falcon diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..4f13051 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +falcon (1.0.0-101pika1) pika; urgency=low + + * Initial release + + -- Ward Nakchbandi Sat, 10 Dec 2022 13:48:00 +0300 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..f0e9c88 --- /dev/null +++ b/debian/control @@ -0,0 +1,19 @@ +Source: falcon +Section: admin +Priority: optional +Maintainer: ferreo +Rules-Requires-Root: no +Build-Depends: + debhelper-compat (= 13), golang-go +Standards-Version: 4.6.1 +Homepage: https://pika-os.com + +Package: falcon +Essential: yes +Architecture: amd64 +Depends: ${misc:Depends}, + ${shlibs:Depends}, + util-linux, + power-profiles-daemon +Provides: falcon +Description: Accelerate your gaming experience with falcon, auto settting tasksets and choosing performance profiles diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..125ed9b --- /dev/null +++ b/debian/copyright @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 PikaOS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..1c09589 --- /dev/null +++ b/debian/rules @@ -0,0 +1,10 @@ +#!/usr/bin/make -f + +# See debhelper(7) (uncomment to enable). +# Output every command that modifies files on the build system. +export DH_VERBOSE = 1 +override_dh_dwz: + echo "disabled" + +%: + dh $@ diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..5f724a8 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module falcon + +go 1.22 + +require golang.org/x/sync v0.8.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e584c1b --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= diff --git a/main.go b/main.go new file mode 100644 index 0000000..abf3193 --- /dev/null +++ b/main.go @@ -0,0 +1,228 @@ +package main + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "sort" + "strconv" + "strings" + + "golang.org/x/sync/errgroup" +) + +type CPUInfo struct { + CPU int `json:"cpu"` + MaxMHz float64 `json:"maxmhz"` +} + +type LSCPUOutput struct { + CPUs []CPUInfo `json:"cpus"` +} + +type CoreFreq struct { + Core int + Freq int +} + +func main() { + args := os.Args[1:] + if len(args) == 0 { + fmt.Println("Please provide a command to run") + os.Exit(1) + } + + eg, ctx := errgroup.WithContext(context.Background()) + canPerf, cores, err := getBestCores() + if err != nil { + eg.Go(func() error { + return runCommand(ctx, false, []int{}, args) + }) + err = eg.Wait() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + os.Exit(0) + } + + eg.Go(func() error { + return runCommand(ctx, canPerf, cores, args) + }) + err = eg.Wait() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + os.Exit(0) +} + +func runCommand(ctx context.Context, canPerf bool, cores []int, args []string) error { + commandToRun := make([]string, 0) + if canPerf { + commandToRun = append(commandToRun, "powerprofilesctl", "launch", "-p", "performance") + } + + if len(cores) > 0 { + coreVals := make([]string, len(cores)) + for i, core := range cores { + coreVals[i] = fmt.Sprint(core) + } + + commandToRun = append(commandToRun, "taskset", "-c", strings.Join(coreVals, ",")) + } + + commandToRun = append(commandToRun, args...) + + cmd := exec.CommandContext(ctx, commandToRun[0], commandToRun[1:]...) + cmd.Env = append(cmd.Environ(), "POSIXLY_CORRECT=1") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + + return cmd.Run() +} + +func getBestCores() (bool, []int, error) { + perfMode, amdPrefCores, amdFlag, highestCores, err := GetPPStatusAndHighestCores() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + if !amdFlag { + return perfMode, highestCores, nil + } + + numAmdCores := len(amdPrefCores) + switch numAmdCores { + case 32: + return perfMode, getHighestCores(16, amdPrefCores), nil + case 24: + return perfMode, getHighestCores(12, amdPrefCores), nil + case 16: + return perfMode, getHighestCores(8, amdPrefCores), nil + default: + return perfMode, getHighestCores(numAmdCores, amdPrefCores), nil + } +} + +func getHighestCores(n int, coreFreqs []CoreFreq) []int { + if n <= 0 { + return []int{} + } + + sort.Slice(coreFreqs, func(i, j int) bool { + return coreFreqs[i].Freq > coreFreqs[j].Freq + }) + + result := make([]int, 0, n) + for i := 0; i < n && i < len(coreFreqs); i++ { + result = append(result, coreFreqs[i].Core) + } + sort.Stable(sort.IntSlice(result)) + + return result +} + +func GetPPStatusAndHighestCores() (bool, []CoreFreq, bool, []int, error) { + var ( + performanceMode bool + amdPstateFreqs []CoreFreq + amdFlag bool + highestCores []int + ) + + g, ctx := errgroup.WithContext(context.Background()) + g.Go(func() error { + cmd := exec.CommandContext(ctx, "powerprofilesctl", "list") + output, err := cmd.Output() + if err != nil { + performanceMode = false + return nil + } + + performanceMode = strings.Contains(string(output), "performance") + return nil + }) + + g.Go(func() error { + pattern := "/sys/devices/system/cpu/cpu*/cpufreq/amd_pstate_prefcore_ranking" + matches, err := filepath.Glob(pattern) + if err != nil { + return fmt.Errorf("failed to glob AMD pstate files: %w", err) + } + + if len(matches) > 0 { + amdFlag = true + } + + for _, match := range matches { + file, err := os.Open(match) + if err != nil { + return fmt.Errorf("failed to open file %s: %w", match, err) + } + defer file.Close() + + parts := strings.Split(match, "/") + cpuPart := parts[len(parts)-3] + coreNum, err := strconv.Atoi(strings.TrimPrefix(cpuPart, "cpu")) + if err != nil { + return fmt.Errorf("failed to parse core number from path %s: %w", match, err) + } + + scanner := bufio.NewScanner(file) + if scanner.Scan() { + value, err := strconv.Atoi(strings.TrimSpace(scanner.Text())) + if err != nil { + return fmt.Errorf("failed to parse value from file %s: %w", match, err) + } + + amdPstateFreqs = append(amdPstateFreqs, CoreFreq{Core: coreNum, Freq: value}) + } + + if err := scanner.Err(); err != nil { + return fmt.Errorf("error reading file %s: %w", match, err) + } + } + return nil + }) + + g.Go(func() error { + cmd := exec.CommandContext(ctx, "lscpu", "-e", "-J") + output, err := cmd.Output() + if err != nil { + return fmt.Errorf("failed to run lscpu command: %w", err) + } + + var lscpuOutput LSCPUOutput + if err := json.Unmarshal(output, &lscpuOutput); err != nil { + return fmt.Errorf("failed to unmarshal lscpu output: %w", err) + } + + var maxMHz float64 + for _, cpu := range lscpuOutput.CPUs { + if cpu.MaxMHz > maxMHz { + maxMHz = cpu.MaxMHz + } + } + + for _, cpu := range lscpuOutput.CPUs { + if cpu.MaxMHz == maxMHz { + highestCores = append(highestCores, cpu.CPU) + } + } + + return nil + }) + + if err := g.Wait(); err != nil { + return false, nil, false, nil, err + } + + return performanceMode, amdPstateFreqs, amdFlag, highestCores, nil +} diff --git a/main.sh b/main.sh new file mode 100755 index 0000000..4a9003e --- /dev/null +++ b/main.sh @@ -0,0 +1,26 @@ +#! /bin/bash + +set -e + +VERSION="1.0.0" + +source ./pika-build-config.sh + +echo "$PIKA_BUILD_ARCH" > pika-build-arch + +# Clone Upstream +mkdir -p ./falcon +cp -rvf ./* ./pikman || true +cd ./falcon + +# Get build deps +apt-get build-dep ./ -y + +# Build package +LOGNAME=root dh_make --createorig -y -l -p falcon_"$VERSION" || echo "dh-make: Ignoring Last Error" +dpkg-buildpackage --no-sign + +# Move the debs to output +cd ../ +mkdir -p ./output +mv ./*.deb ./output/ diff --git a/pika-build-config/amd64-v3.sh b/pika-build-config/amd64-v3.sh new file mode 100755 index 0000000..10285b4 --- /dev/null +++ b/pika-build-config/amd64-v3.sh @@ -0,0 +1,10 @@ +#! /bin/bash +export PIKA_BUILD_ARCH="amd64-v3" +export DEBIAN_FRONTEND="noninteractive" +export DEB_BUILD_MAINT_OPTIONS="optimize=+lto -march=x86-64-v3 -O3 -flto -fuse-linker-plugin -falign-functions=32" +export DEB_CFLAGS_MAINT_APPEND="-march=x86-64-v3 -O3 -flto -fuse-linker-plugin -falign-functions=32" +export DEB_CPPFLAGS_MAINT_APPEND="-march=x86-64-v3 -O3 -flto -fuse-linker-plugin -falign-functions=32" +export DEB_CXXFLAGS_MAINT_APPEND="-march=x86-64-v3 -O3 -flto -fuse-linker-plugin -falign-functions=32" +export DEB_LDFLAGS_MAINT_APPEND="-march=x86-64-v3 -O3 -flto -fuse-linker-plugin -falign-functions=32" +export DEB_BUILD_OPTIONS="nocheck notest terse" +export DPKG_GENSYMBOLS_CHECK_LEVEL=0 diff --git a/pika-build-config/i386.sh b/pika-build-config/i386.sh new file mode 100755 index 0000000..7629d66 --- /dev/null +++ b/pika-build-config/i386.sh @@ -0,0 +1,5 @@ +#! /bin/bash +export PIKA_BUILD_ARCH="i386" +export DEBIAN_FRONTEND="noninteractive" +export DEB_BUILD_OPTIONS="nocheck notest terse" +export DPKG_GENSYMBOLS_CHECK_LEVEL=0