hyperctl 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bd42c9d0ce6995577336ac5d079c38b2dbcf5f0b
4
+ data.tar.gz: d71520d05bb74c3bb584868fbdb9663be22b7b6c
5
+ SHA512:
6
+ metadata.gz: d90ca4e32248d536a58eb2a9f463e69caa9cbf8617d2d9e0b86358f38f95e2727292476c0ac94ba5acceadf373ee4c6db6075f7cacbd19acfbad78f63bc856d8
7
+ data.tar.gz: f3fdfd9fbc3ee5c449efddcd8af2daf59ed74123efe78769add4bffd3d9e7ae87942133907d3504cc65c51fb7a8f4783f2313056f63eaa2d2e511b2c24d4d523
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ .bundle
2
+ doc/
3
+ *.gem
4
+ Gemfile.lock
5
+ *.orig
6
+ *.patch
7
+ *.rej
8
+ .ruby-version
9
+ *.swp
10
+ tmp/aruba/
11
+ .vagrant
12
+ .yardoc
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1.5
7
+ script: "bundle exec rake spec"
8
+ notifications:
9
+ email: false
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,200 @@
1
+ Ruby hyperctl Gem
2
+ =================
3
+
4
+ [![Build Status](https://travis-ci.org/jhoblitt/hyperctl.png)](https://travis-ci.org/jhoblitt/hyperctl)
5
+
6
+ #### Table of Contents
7
+
8
+ 1. [Summary](#summary)
9
+ 2. [Description](#description)
10
+ 3. [Install](#install)
11
+ 4. [Usage](#usage)
12
+ 5. [Versioning](#versioning)
13
+ 6. [Support](#support)
14
+ 7. [Contributing](#contributing)
15
+ 8. [See Also](#see-also)
16
+
17
+ Summary
18
+ -------
19
+
20
+ A utility for enabling/disabling hyperthreading
21
+
22
+
23
+ Description
24
+ -----------
25
+
26
+ This Gem provides a simple cli utility named `hyperctl` that can check the
27
+ status of and [with appropriate permissions] enable/disable
28
+ hyperthreading/SMT/sibling cores on Linux via the
29
+ [`sysfs`](https://www.kernel.org/doc/Documentation/filesystems/sysfs.txt)
30
+ pseudo filesystem. It should be compatible with most modern Linux
31
+ distributions as long as `sysfs` is mounted at `/sysfs`.
32
+
33
+
34
+ Install
35
+ -------
36
+
37
+ ### via rubygems
38
+
39
+ gem install hyperctl
40
+
41
+ ### from git repo with bundler
42
+
43
+ bundle install
44
+ bundle exec rake install
45
+
46
+
47
+ Usage
48
+ -----
49
+
50
+ ### `hyperctl`
51
+
52
+ Usage:
53
+ hyperctl
54
+ hyperctl (--enable | --disable) [--quiet]
55
+ hyperctl --status (--enable | --disable) [--quiet]
56
+ hyperctl -h | --help
57
+ hyperctl --version
58
+
59
+ Options:
60
+ --enable Enable hyperthreading.
61
+ --disable Diable hyperthreading.
62
+ --status Report hyperthreading state.
63
+ --quiet Suppress stdout.
64
+ --version Show version.
65
+ -h --help Show this screen.
66
+
67
+ #### Exit codes
68
+
69
+ * `0` - success
70
+ * `1` - option parsing related error
71
+ * `2` - system error (Eg., permission denied)
72
+ * `3` - status does not match desired state
73
+
74
+ #### no options
75
+
76
+ Prints the current status of hyperthreading on the system to the `stdout`.
77
+ Exits with `0` if no errors were encountered.
78
+
79
+ $ hyperctl
80
+ cpu0 : enabled - hypertheading: enabled
81
+ cpu1 : enabled - hypertheading: enabled
82
+ cpu2 : enabled - hypertheading: enabled
83
+ cpu3 : enabled - hypertheading: enabled
84
+ cpu4 : enabled - hypertheading: enabled
85
+ cpu5 : enabled - hypertheading: enabled
86
+ cpu6 : enabled - hypertheading: enabled
87
+ cpu7 : enabled - hypertheading: enabled
88
+
89
+ #### `(--enable | --disable) [--quiet]`
90
+
91
+ Attempt to enable or disable all hyperthread/SMT/sibling cores on the system.
92
+ Exits with `0` upon success, `2` for system errors, or `3` for state change
93
+ failures (unlikely).
94
+
95
+ With appropriate permissions to modify the appropriate sysfs entries:
96
+
97
+ $ sudo hyperctl --disable
98
+ cpu0 : enabled - hypertheading: disabled
99
+ cpu1 : enabled - hypertheading: disabled
100
+ cpu2 : enabled - hypertheading: disabled
101
+ cpu3 : enabled - hypertheading: disabled
102
+ cpu4 : disabled - hypertheading: disabled
103
+ cpu5 : disabled - hypertheading: disabled
104
+ cpu6 : disabled - hypertheading: disabled
105
+ cpu7 : disabled - hypertheading: disabled
106
+ $ echo $?
107
+ 0
108
+
109
+ Without appropriate permissions:
110
+
111
+ $ hyperctl --disable
112
+ Permission denied @ rb_sysopen - /sys/devices/system/cpu/cpu4/online
113
+ $ echo $?
114
+ 2
115
+
116
+ #### `--status (--enable | --disable) [--quiet]`
117
+
118
+ Checks the status of hyperthread/SMT/sibling cores on the system. Exits with
119
+ `0` if the state matches the `(--enable | --disable)` option, otherwise with
120
+ `3`
121
+
122
+ System already in `--disable` state:
123
+
124
+ $ sudo hyperctl --status --disable
125
+ cpu0 : enabled - hypertheading: disabled
126
+ cpu1 : enabled - hypertheading: disabled
127
+ cpu2 : enabled - hypertheading: disabled
128
+ cpu3 : enabled - hypertheading: disabled
129
+ cpu4 : disabled - hypertheading: disabled
130
+ cpu5 : disabled - hypertheading: disabled
131
+ cpu6 : disabled - hypertheading: disabled
132
+ cpu7 : disabled - hypertheading: disabled
133
+ $ echo $?
134
+ 0
135
+
136
+ System not in `--enable` state:
137
+
138
+ $ sudo hyperctl --status --enable
139
+ cpu0 : enabled - hypertheading: disabled
140
+ cpu1 : enabled - hypertheading: disabled
141
+ cpu2 : enabled - hypertheading: disabled
142
+ cpu3 : enabled - hypertheading: disabled
143
+ cpu4 : disabled - hypertheading: disabled
144
+ cpu5 : disabled - hypertheading: disabled
145
+ cpu6 : disabled - hypertheading: disabled
146
+ cpu7 : disabled - hypertheading: disabled
147
+ $ echo $?
148
+ 3
149
+
150
+ #### `[--quiet]`
151
+
152
+ Suppresses the `stdout` status message. Intended for usage from scripts.
153
+
154
+ $ sudo hyperctl --status --enable --quiet
155
+ $ echo $?
156
+ 3
157
+
158
+ $ sudo hyperctl --status --disable --quiet
159
+ $ echo $?
160
+ 0
161
+
162
+
163
+ Versioning
164
+ ----------
165
+
166
+ This Gem is versioned according to the [Semantic Versioning
167
+ 2.0.0](http://semver.org/spec/v2.0.0.html) specification.
168
+
169
+
170
+ Support
171
+ -------
172
+
173
+ Please log tickets and issues at [github](https://github.com/jhoblitt/hyperctl)
174
+
175
+
176
+ Contributing
177
+ ------------
178
+
179
+ 1. Fork it on github
180
+ 2. Make a local clone of your fork
181
+ 3. Create a topic branch. Eg, `feature/mousetrap`
182
+ 4. Make/commit changes
183
+ * Commit messages should be in
184
+ [imperative tense](http://git-scm.com/book/ch5-2.html)
185
+ * Check that `Rspec` unit tests are not broken and coverage is added for
186
+ new features - `bundle exec rake spec`
187
+ * Documentation of API/features is updated as appropriate in the README
188
+ 5. When the feature is complete, rebase / squash the branch history as
189
+ necessary to remove "fix typo", "oops", "whitespace" and other trivial
190
+ commits
191
+ 6. Push the topic branch to github
192
+ 7. Open a Pull Request (PR) from the *topic branch* onto parent repo's `master`
193
+ branch
194
+
195
+
196
+ See Also
197
+ --------
198
+
199
+ * [Linux sysfs docs](https://www.kernel.org/doc/Documentation/filesystems/sysfs.txt)
200
+ * [Linux cpu-hotplug docs](https://www.kernel.org/doc/Documentation/cpu-hotplug.txt)
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+ require 'yard'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ YARD::Rake::YardocTask.new do |t|
8
+ t.files = ['lib/**/*.rb']
9
+ t.stats_options = ['--list-undoc']
10
+ end
11
+
12
+ task :default => [
13
+ :spec,
14
+ ]
data/bin/hyperctl ADDED
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'hyperctl'
4
+ require "docopt"
5
+
6
+ doc = <<DOCOPT
7
+ #{__FILE__}
8
+
9
+ Usage:
10
+ #{__FILE__}
11
+ #{__FILE__} (--enable | --disable) [--quiet]
12
+ #{__FILE__} --status (--enable | --disable) [--quiet]
13
+ #{__FILE__} -h | --help
14
+ #{__FILE__} --version
15
+
16
+ Options:
17
+ --enable Enable hyperthreading.
18
+ --disable Diable hyperthreading.
19
+ --status Report hyperthreading state.
20
+ --quiet Suppress stdout.
21
+ --version Show version.
22
+ -h --help Show this screen.
23
+
24
+ DOCOPT
25
+
26
+
27
+ @options = {}
28
+ begin
29
+ @options = Docopt::docopt(doc)
30
+ rescue Docopt::Exit => e
31
+ puts e.message
32
+ exit 1
33
+ end
34
+
35
+ if @options['--version']
36
+ puts Hyperctl::VERSION
37
+ exit 0
38
+ end
39
+
40
+ hctl = Hyperctl::Sysfs.new
41
+
42
+ exit_code = 0
43
+
44
+ begin
45
+ if @options['--status']
46
+ exit_code = check_status(hctl)
47
+ elsif @options['--enable'] || @options['--disable']
48
+ exit_code = change_state(hctl)
49
+ end
50
+
51
+ unless @options['--quiet']
52
+ text = Hyperctl.status(hctl)
53
+ puts text
54
+ end
55
+ rescue Errno::EACCES => e
56
+ puts e.message
57
+ exit 2
58
+ end
59
+
60
+ exit exit_code
61
+
62
+ BEGIN {
63
+ def change_state(hctl)
64
+ if @options['--enable']
65
+ Hyperctl.enable(hctl)
66
+ hctl.refresh
67
+ return check_status(hctl)
68
+ end
69
+
70
+ if @options['--disable']
71
+ Hyperctl.disable(hctl)
72
+ hctl.refresh
73
+ return check_status(hctl)
74
+ end
75
+
76
+ raise
77
+ end
78
+
79
+ def check_status(hctl)
80
+ if @options['--enable']
81
+ return hctl.all_cores_enabled? ? 0 : 3
82
+ end
83
+
84
+ if @options['--disable']
85
+ return hctl.all_siblings_disabled? ? 0 : 3
86
+ end
87
+
88
+ raise
89
+ end
90
+ }
data/hyperctl.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
2
+ require 'hyperctl/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'hyperctl'
6
+ s.version = Hyperctl::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ['Joshua Hoblitt']
9
+ s.email = ['jhoblitt@cpan.org']
10
+ s.homepage = 'https://github.com/jhoblitt/hyperctl'
11
+ s.summary = %q{A utility for enabling/disabling hyperthreading}
12
+ s.description = %q{This Gem provides a simple cli utility named `hyperctl`
13
+ that can check the status of and [with appropriate permissions] enable/disable
14
+ hyperthreading/SMT/sibling cores on Linux via the `sysfs` pseudo filesystem.
15
+ It should be compatible with most modern Linux distributions as long as `sysfs` is mounted at `/sysfs`.}
16
+ s.license = 'Apache 2.0'
17
+
18
+ s.required_ruby_version = '>= 1.8.7'
19
+ s.add_runtime_dependency('docopt', '~> 0.5.0')
20
+ s.add_development_dependency('rspec', '~> 3.0')
21
+ s.add_development_dependency('rspec-expectations', '~> 3.0')
22
+ s.add_development_dependency('rspec-mocks', '~> 3.0')
23
+ s.add_development_dependency('rake', '~> 10.0')
24
+ s.add_development_dependency('fakefs', '~> 0.5.2')
25
+ s.add_development_dependency('mocha', '~> 1.0')
26
+ s.add_development_dependency('yard', '~> 0.8')
27
+
28
+ s.rubygems_version = '>= 1.6.1'
29
+ s.files = `git ls-files`.split("\n")
30
+ s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
31
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
32
+ s.require_path = 'lib'
33
+ end
@@ -0,0 +1,189 @@
1
+ class Hyperctl::Sysfs
2
+ attr_accessor :cpu_info
3
+
4
+ # @api private
5
+ def initialize
6
+ @cpu_info = refresh
7
+ end
8
+
9
+ # Refresh the `cpu_info` [Hash] of the current CPU states
10
+ #
11
+ # @return [Hash] of cpu status
12
+ # @api public
13
+ def refresh
14
+ info = {}
15
+
16
+ # get a listing of all cpu cores
17
+ cpu_dirs = Dir.glob('/sys/devices/system/cpu/cpu[0-9]*')
18
+
19
+ cpu_dirs.each do |d|
20
+ # find the "name" of the cpu based on the sysfs dir. Eg, cpu15
21
+ cpu_name = File.basename d
22
+ cpu_idx = cpu_name.to_sym
23
+ info[cpu_idx] = { :name => cpu_name }
24
+
25
+ # find the numeric core_id. Eg, 15 in sysfs as
26
+ # /sys/devices/system/cpu/cpu3/topology/core_id but we can parse it from
27
+ # the path
28
+ core_id = cpu_name.match(/cpu(\d+)/)[1].to_i
29
+ info[cpu_idx][:core_id] = core_id
30
+
31
+ # is the cpu online?
32
+ # if a CPU is online, /sys/devices/system/cpu/cpu1/online will be 1,
33
+ # otherwise 0. cpu0 appears to be special and does not have the online
34
+ # sysfs entry on any of the systems I inspected. I suspect that it might
35
+ # get this attribute if CONFIG_BOOTPARAM_HOTPLUG_CPU0 is enabled per
36
+ # https://www.kernel.org/doc/Documentation/cpu-hotplug.txt
37
+ path = File.join(d, 'online')
38
+ online = false
39
+ if File.exist?(path)
40
+ online = to_bool(File.read(path).chomp)
41
+ elsif core_id == 0
42
+ # cpu0 gets a special pass if the online attr is missing
43
+ online = true
44
+ end
45
+ info[cpu_idx][:online] = online
46
+
47
+ next unless online
48
+
49
+ # does the cpu have any [SMT] siblings?
50
+ # The attr /sys/devices/system/cpu/cpu6/topology/thread_siblings_list
51
+ # will list all siblings including the cpu's own core_id This attr is not
52
+ # present if the cpu is offline This attr is not present under EL5.x
53
+ # (2.6.18-164.el5PAE) on the one system I inspected that appears to have
54
+ # HT disabled in the bios (/proc/cpuinfo shows the ht cpu flag but
55
+ # there's no siblings list)
56
+ path = File.join(d, 'topology/thread_siblings_list')
57
+ if File.exist?(path)
58
+ sibs = File.read(path).chomp.split(',')
59
+ # convert core_id(s) to be numeric
60
+ sibs.map! {|s| s.to_i }
61
+ # remove the cpu's core_id from the list
62
+ sibs = sibs - [ core_id ]
63
+ unless sibs.empty?
64
+ info[cpu_idx][:thread_siblings_list] = sibs
65
+ end
66
+ end
67
+ end
68
+
69
+ @cpu_info = info
70
+ end
71
+
72
+ # List of all CPUs in system by numeric core id
73
+ #
74
+ # @return [Array<Integer>] of core ids
75
+ # @api public
76
+ def cores
77
+ cores = []
78
+ cpu_info.each_key.sort_by {|k| cpu_info[k][:core_id] }.each do |k|
79
+ cores << cpu_info[k][:core_id]
80
+ end
81
+
82
+ return cores
83
+ end
84
+
85
+ # List of online CPUs in system by numeric core id
86
+ #
87
+ # @return [Array<Integer>] of core ids
88
+ # @api public
89
+ def online_cores
90
+ cores = []
91
+ cpu_info.each_key.sort_by {|k| cpu_info[k][:core_id] }.each do |k|
92
+ core_id = cpu_info[k][:core_id]
93
+ if cpu_info[k][:online] == true
94
+ cores << core_id
95
+ end
96
+ end
97
+
98
+ return cores
99
+ end
100
+
101
+ # List of offline CPUs in system by numeric core id
102
+ #
103
+ # @return [Array<Integer>] of core ids
104
+ # @api public
105
+ def offline_cores
106
+ cores = []
107
+ cpu_info.each_key.sort_by {|k| cpu_info[k][:core_id] }.each do |k|
108
+ core_id = cpu_info[k][:core_id]
109
+ if cpu_info[k][:online] == false
110
+ cores << core_id
111
+ end
112
+ end
113
+
114
+ return cores
115
+ end
116
+
117
+ # List of sibling (aka hyperthread/SMT) CPUs in system by numeric core id
118
+ #
119
+ # @return [Array<Integer>] of core ids
120
+ # @api public
121
+ def sibling_cores
122
+ cores = []
123
+ checked_cores = []
124
+ cpu_info.each_key.sort_by {|k| cpu_info[k][:core_id] }.each do |k|
125
+ cpu = cpu_info[k]
126
+ checked_cores << cpu[:core_id]
127
+
128
+ if cpu.has_key?(:thread_siblings_list)
129
+ (cpu[:thread_siblings_list] - checked_cores).each do |core_id|
130
+ # check to see if the core is already disabled
131
+ # XXX this probably isn't nessicary as a disabled core appears to #
132
+ # never be listed as a sibiling
133
+ if cpu_info[k][:online] == true
134
+ cores << core_id
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ return cores
141
+ end
142
+
143
+ # Are all phyical/hyperthread/SMT/sibling cores enabled?
144
+ #
145
+ # @return [Bool] of status
146
+ # @api public
147
+ def all_cores_enabled?
148
+ cores.count == online_cores.count
149
+ end
150
+
151
+ # Are all hyperthread/SMT/sibling cores Disabled?
152
+ #
153
+ # @return [Bool] of status
154
+ # @api public
155
+ def all_siblings_disabled?
156
+ sibling_cores.empty?
157
+ end
158
+
159
+ # Enable a CPU by numeric core id
160
+ #
161
+ # @param core_id [Integer]
162
+ # @api public
163
+ def self.enable_core(core_id)
164
+ set_core(core_id, '1')
165
+ end
166
+
167
+ # Disable a CPU by numeric core id
168
+ #
169
+ # @param core_id [Integer]
170
+ # @api public
171
+ def self.disable_core(core_id)
172
+ set_core(core_id, '0')
173
+ end
174
+
175
+ private
176
+
177
+ def to_bool(s)
178
+ return true if s =~ /^1$/
179
+ return false
180
+ end
181
+
182
+ def self.set_core(core_id, state)
183
+ path = File.join('/sys/devices/system/cpu', "cpu#{core_id.to_s}", 'online')
184
+ # doesn't work in ruby 1.8.7: File.write(path, '0')
185
+ File.open(path, 'w') do |f|
186
+ f.write(state)
187
+ end
188
+ end
189
+ end # class Hyperctl::Sysfs
@@ -0,0 +1,3 @@
1
+ module Hyperctl
2
+ VERSION = '0.1.0'
3
+ end
data/lib/hyperctl.rb ADDED
@@ -0,0 +1,43 @@
1
+ require 'hyperctl/sysfs'
2
+
3
+ module Hyperctl
4
+ # Enable all sibling cores
5
+ #
6
+ # @param hctl [Hyperctl::Sysfs] object
7
+ # @api public
8
+ def self.enable(hctl)
9
+ # as far as I can tell, there's no way to discover the topology information
10
+ # for which cores and siblings if either of them is disabled. So we are
11
+ # just trying to enable everything that is disabled...
12
+ cores = hctl.offline_cores
13
+ cores.each {|core_id| Hyperctl::Sysfs.enable_core(core_id) }
14
+ end
15
+
16
+ # Disable all sibling cores
17
+ #
18
+ # @param hctl [Hyperctl::Sysfs] object
19
+ # @api public
20
+ def self.disable(hctl)
21
+ cores = hctl.sibling_cores
22
+ cores.each {|core_id| Hyperctl::Sysfs.disable_core(core_id) }
23
+ end
24
+
25
+ # Generate a pretty formatted string of sibling core status
26
+ #
27
+ # @param hctl [Hyperctl::Sysfs] object
28
+ # @return [String] the generated string
29
+ # @api public
30
+ def self.status(hctl)
31
+ cpu_info = hctl.cpu_info
32
+ text = ""
33
+ cpu_info.each_key.sort_by {|k| cpu_info[k][:core_id] }.each do |k|
34
+ cpu = cpu_info[k]
35
+ state = cpu[:online] ? 'enabled' : 'disabled'
36
+ ht = cpu.has_key?(:thread_siblings_list) ? 'enabled' : 'disabled'
37
+ text << "#{sprintf('%-5s',k.to_s)}: #{sprintf('%-8s', state)}"
38
+ text << " - hypertheading: #{ht}\n"
39
+ end
40
+
41
+ return text
42
+ end
43
+ end # module Hyperctl
@@ -0,0 +1,123 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hyperctl do
4
+ let(:hctl) { Hyperctl::Sysfs.new }
5
+
6
+ describe '#status' do
7
+ context '8 physical cores with HT enabled' do
8
+ include_context "cpuinfo_8core_w_ht"
9
+
10
+ text =<<EOS
11
+ cpu0 : enabled - hypertheading: enabled
12
+ cpu1 : enabled - hypertheading: enabled
13
+ cpu2 : enabled - hypertheading: enabled
14
+ cpu3 : enabled - hypertheading: enabled
15
+ cpu4 : enabled - hypertheading: enabled
16
+ cpu5 : enabled - hypertheading: enabled
17
+ cpu6 : enabled - hypertheading: enabled
18
+ cpu7 : enabled - hypertheading: enabled
19
+ cpu8 : enabled - hypertheading: enabled
20
+ cpu9 : enabled - hypertheading: enabled
21
+ cpu10: enabled - hypertheading: enabled
22
+ cpu11: enabled - hypertheading: enabled
23
+ cpu12: enabled - hypertheading: enabled
24
+ cpu13: enabled - hypertheading: enabled
25
+ cpu14: enabled - hypertheading: enabled
26
+ cpu15: enabled - hypertheading: enabled
27
+ EOS
28
+ it do
29
+ hctl.stubs(:cpu_info).returns(info)
30
+ expect(Hyperctl.status(hctl)).to eq(text)
31
+ end
32
+ end # 8 physical cores with HT enabled
33
+
34
+ context '12 physical cores with HT disabled' do
35
+ include_context "cpuinfo_12core_wo_ht"
36
+ text =<<EOS
37
+ cpu0 : enabled - hypertheading: disabled
38
+ cpu1 : enabled - hypertheading: disabled
39
+ cpu2 : enabled - hypertheading: disabled
40
+ cpu3 : enabled - hypertheading: disabled
41
+ cpu4 : enabled - hypertheading: disabled
42
+ cpu5 : enabled - hypertheading: disabled
43
+ cpu6 : enabled - hypertheading: disabled
44
+ cpu7 : enabled - hypertheading: disabled
45
+ cpu8 : enabled - hypertheading: disabled
46
+ cpu9 : enabled - hypertheading: disabled
47
+ cpu10: enabled - hypertheading: disabled
48
+ cpu11: enabled - hypertheading: disabled
49
+ cpu12: disabled - hypertheading: disabled
50
+ cpu13: disabled - hypertheading: disabled
51
+ cpu14: disabled - hypertheading: disabled
52
+ cpu15: disabled - hypertheading: disabled
53
+ cpu16: disabled - hypertheading: disabled
54
+ cpu17: disabled - hypertheading: disabled
55
+ cpu18: disabled - hypertheading: disabled
56
+ cpu19: disabled - hypertheading: disabled
57
+ cpu20: disabled - hypertheading: disabled
58
+ cpu21: disabled - hypertheading: disabled
59
+ cpu22: disabled - hypertheading: disabled
60
+ cpu23: disabled - hypertheading: disabled
61
+ EOS
62
+ it do
63
+ hctl.stubs(:cpu_info).returns(info)
64
+
65
+ expect(Hyperctl.status(hctl)).to eq(text)
66
+ end
67
+ end # 12 physical cores with HT disabled
68
+ end #status
69
+
70
+ describe '#disable' do
71
+ context '8 physical cores with HT enabled' do
72
+ include_context "cpuinfo_8core_w_ht"
73
+
74
+ it 'should turn cores off' do
75
+ hctl.stubs(:cpu_info).returns(info)
76
+ 8.upto(15).each do |core_id|
77
+ Hyperctl::Sysfs.expects(:disable_core).with(core_id).once
78
+ end
79
+
80
+ Hyperctl.disable(hctl)
81
+ end
82
+ end # 8 physical cores with HT enabled
83
+
84
+ context '12 physical cores with HT disabled' do
85
+ include_context "cpuinfo_12core_wo_ht"
86
+
87
+ it 'should do nothing' do
88
+ # all SMT cores are already disabled
89
+ hctl.stubs(:cpu_info).returns(info)
90
+ Hyperctl::Sysfs.expects(:disable_core).never
91
+
92
+ Hyperctl.disable(hctl)
93
+ end
94
+ end # 12 physical cores with HT disabled
95
+ end #disable
96
+
97
+ describe '#enable' do
98
+ context '8 physical cores with HT enabled' do
99
+ include_context "cpuinfo_8core_w_ht"
100
+
101
+ it 'should do nothing' do
102
+ # all SMT cores are already enabled
103
+ hctl.stubs(:cpu_info).returns(info)
104
+ Hyperctl::Sysfs.expects(:enable_core).never
105
+
106
+ Hyperctl.enable(hctl)
107
+ end
108
+ end # 8 physical cores with HT enabled
109
+
110
+ context '12 physical cores with HT disabled' do
111
+ include_context "cpuinfo_12core_wo_ht"
112
+
113
+ it 'should turn cores on' do
114
+ hctl.stubs(:cpu_info).returns(info)
115
+ 12.upto(23).each do |core_id|
116
+ Hyperctl::Sysfs.expects(:enable_core).with(core_id).once
117
+ end
118
+
119
+ Hyperctl.enable(hctl)
120
+ end
121
+ end # 12 physical cores with HT disabled
122
+ end #enable
123
+ end
@@ -0,0 +1,154 @@
1
+ require 'spec_helper'
2
+ # pp must be required before fakefs
3
+ # see: https://github.com/defunkt/fakefs/issues/99
4
+ require 'pp'
5
+ require 'fakefs/spec_helpers'
6
+
7
+ describe Hyperctl::Sysfs do
8
+ let(:hctl) { Hyperctl::Sysfs.new }
9
+ include FakeFS::SpecHelpers
10
+
11
+ context 'on a 8 physical core w/ HT enabled system' do
12
+ include_context "sysfs_8core_w_ht"
13
+ include_context "cpuinfo_8core_w_ht"
14
+
15
+ context 'fakefs sanity checks' do
16
+ it { expect(File.exists?(sysfs('cpu1', 'online'))).to be true }
17
+ it { expect(File.exists?(sysfs('cpu1', 'topology/thread_siblings_list'))).to be true }
18
+ it { expect(sysfs('cpu1', 'topology/thread_siblings_list')).to contain(/1,9/) }
19
+ end
20
+
21
+ describe '#new' do
22
+ it 'finds all the cores' do
23
+ expect(hctl.cpu_info).to eq(info)
24
+ end
25
+ end
26
+
27
+ describe '#disable_core' do
28
+ it 'disbles core_id 15' do
29
+ Hyperctl::Sysfs.disable_core(15)
30
+
31
+ expect(sysfs('cpu15', 'online')).to contain(/^0$/)
32
+ end
33
+ end
34
+
35
+ context '#cores' do
36
+ it 'lists all cores' do
37
+ expect(hctl.cores).to eq((0 .. 15).to_a)
38
+ end
39
+ end
40
+
41
+ context '#online_cores' do
42
+ it 'lists all enabled cores' do
43
+ expect(hctl.online_cores).to eq((0 .. 15).to_a)
44
+ end
45
+ end
46
+
47
+ context '#offline_cores' do
48
+ it 'lists all disabled cores' do
49
+ expect(hctl.offline_cores).to eq []
50
+ end
51
+ end
52
+
53
+ context '#sibling_cores' do
54
+ it 'lists all smt cores' do
55
+ expect(hctl.sibling_cores).to eq((8 .. 15).to_a)
56
+ end
57
+ end
58
+
59
+ context '#all_cores_enabled?' do
60
+ it 'returns true' do
61
+ expect(hctl.all_cores_enabled?).to be true
62
+ end
63
+ end
64
+
65
+ context '#all_siblings_disabled?' do
66
+ it 'returns false' do
67
+ expect(hctl.all_siblings_disabled?).to be false
68
+ end
69
+ end
70
+
71
+ context '#enable_core' do
72
+ it 'enables core_id 15' do
73
+ Hyperctl::Sysfs.enable_core(15)
74
+
75
+ expect(sysfs('cpu15', 'online')).to contain(/^1$/)
76
+ end
77
+ end
78
+ end # on a 8 physical core w/ HT enabled system
79
+
80
+ context 'on a 12 physical core w/ HT diabled system' do
81
+ include_context "sysfs_12core_wo_ht"
82
+ include_context "cpuinfo_12core_wo_ht"
83
+
84
+ context 'fakefs sanity checks' do
85
+ it { expect(File.exists?(sysfs('cpu0', 'online'))).to be false }
86
+ it { expect(File.exists?(sysfs('cpu0', 'topology/thread_siblings_list'))).to be true }
87
+
88
+ it { expect(File.exists?(sysfs('cpu1', 'online'))).to be true }
89
+ it { expect(File.exists?(sysfs('cpu1', 'topology/thread_siblings_list'))).to be true }
90
+
91
+ it { expect(File.exists?(sysfs('cpu23', 'online'))).to be true }
92
+ it { expect(File.exists?(sysfs('cpu23', 'topology/thread_siblings_list'))).to be false }
93
+ end
94
+
95
+ context '#new' do
96
+ it 'finds all the cores' do
97
+ hctl = Hyperctl::Sysfs.new
98
+ expect(hctl.cpu_info).to eq(info)
99
+ end
100
+ end
101
+
102
+ context '#disable_core' do
103
+ it 'disbles core_id 15' do
104
+ Hyperctl::Sysfs.disable_core(15)
105
+
106
+ expect(sysfs('cpu15', 'online')).to contain(/^0$/)
107
+ end
108
+ end
109
+
110
+ context '#cores' do
111
+ it 'lists all cores' do
112
+ expect(hctl.cores).to eq((0 .. 23).to_a)
113
+ end
114
+ end
115
+
116
+ context '#online_cores' do
117
+ it 'lists all enabled cores' do
118
+ expect(hctl.online_cores).to eq((0 .. 11).to_a)
119
+ end
120
+ end
121
+
122
+ context '#offline_cores' do
123
+ it 'lists all disabled cores' do
124
+ expect(hctl.offline_cores).to eq((12 .. 23).to_a)
125
+ end
126
+ end
127
+
128
+ context '#sibling_cores' do
129
+ it 'lists all smt cores' do
130
+ expect(hctl.sibling_cores).to eq []
131
+ end
132
+ end
133
+
134
+ context '#all_cores_enabled?' do
135
+ it 'returns false' do
136
+ expect(hctl.all_cores_enabled?).to be false
137
+ end
138
+ end
139
+
140
+ context '#all_siblings_disabled?' do
141
+ it 'returns true' do
142
+ expect(hctl.all_siblings_disabled?).to be true
143
+ end
144
+ end
145
+
146
+ context '#enable_core' do
147
+ it 'enables core_id 15' do
148
+ Hyperctl::Sysfs.enable_core(15)
149
+
150
+ expect(sysfs('cpu15', 'online')).to contain(/^1$/)
151
+ end
152
+ end
153
+ end # on a 12 physical core w/ HT disabled system
154
+ end
@@ -0,0 +1,102 @@
1
+ require 'hyperctl'
2
+
3
+ RSpec.configure do |config|
4
+ config.mock_framework = :mocha
5
+ end
6
+
7
+ def sysfs(cpu_name, attr)
8
+ File.join('/sys/devices/system/cpu/', cpu_name, attr)
9
+ end
10
+
11
+ def mksysfs(cpu_name, attr, value)
12
+ path = sysfs(cpu_name, attr)
13
+ dir = File.dirname path
14
+ FileUtils.mkdir_p dir
15
+ File.open(path, 'w+') do |f|
16
+ f.write(value)
17
+ end
18
+ end
19
+
20
+ RSpec::Matchers.define :contain do |content|
21
+ match do |path|
22
+ File.read(path) =~ content
23
+ end
24
+ end
25
+
26
+ # 2.6.32-431.5.1.el6.x86_64
27
+ # 2 x Intel(R) Xeon(R) CPU E5-2643 0 @ 3.30GHz
28
+ RSpec.shared_context 'sysfs_8core_w_ht' do
29
+ before do
30
+ 0.upto(15).each do |core_id|
31
+ next if core_id == 0
32
+ mksysfs("cpu#{core_id}", 'online', "1\n")
33
+ end
34
+ 0.upto(7).each do |core_id|
35
+ mksysfs("cpu#{core_id}", 'topology/thread_siblings_list', "#{core_id},#{core_id + 8}\n")
36
+ end
37
+ 8.upto(15).each do |core_id|
38
+ mksysfs("cpu#{core_id}", 'topology/thread_siblings_list', "#{core_id},#{core_id - 8}\n")
39
+ end
40
+ end
41
+ end
42
+
43
+ RSpec.shared_context 'cpuinfo_8core_w_ht' do
44
+ info = {}
45
+ 0.upto(7).each do |core_id|
46
+ name = "cpu#{core_id}"
47
+ info[name.to_sym] = {
48
+ :core_id => core_id,
49
+ :online => true,
50
+ :thread_siblings_list => [ core_id + 8 ],
51
+ :name => name,
52
+ }
53
+ end
54
+ 8.upto(15).each do |core_id|
55
+ name = "cpu#{core_id}"
56
+ info[name.to_sym] = {
57
+ :core_id => core_id,
58
+ :online => true,
59
+ :thread_siblings_list => [ core_id - 8 ],
60
+ :name => name,
61
+ }
62
+ end
63
+
64
+ let(:info) { info }
65
+ end
66
+
67
+ # 2.6.32-431.5.1.el6.x86_64
68
+ # 2 x Intel(R) Xeon(R) CPU X5675 @ 3.07GHz
69
+ RSpec.shared_context 'sysfs_12core_wo_ht' do
70
+ before do
71
+ 0.upto(11).each do |core_id|
72
+ mksysfs("cpu#{core_id}", 'topology/thread_siblings_list', "#{core_id}\n")
73
+ next if core_id == 0
74
+ mksysfs("cpu#{core_id}", 'online', "1\n")
75
+ end
76
+ 12.upto(23).each do |core_id|
77
+ mksysfs("cpu#{core_id}", 'online', "0\n")
78
+ end
79
+ end
80
+ end
81
+
82
+ RSpec.shared_context 'cpuinfo_12core_wo_ht' do
83
+ info = {}
84
+ 0.upto(11).each do |core_id|
85
+ name = "cpu#{core_id}"
86
+ info[name.to_sym] = {
87
+ :core_id => core_id,
88
+ :online => true,
89
+ :name => name,
90
+ }
91
+ end
92
+ 12.upto(23).each do |core_id|
93
+ name = "cpu#{core_id}"
94
+ info[name.to_sym] = {
95
+ :core_id => core_id,
96
+ :online => false,
97
+ :name => name,
98
+ }
99
+ end
100
+
101
+ let(:info) { info }
102
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hyperctl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Joshua Hoblitt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: docopt
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.5.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.5.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-expectations
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-mocks
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: fakefs
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.5.2
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.5.2
97
+ - !ruby/object:Gem::Dependency
98
+ name: mocha
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: yard
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.8'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.8'
125
+ description: |-
126
+ This Gem provides a simple cli utility named `hyperctl`
127
+ that can check the status of and [with appropriate permissions] enable/disable
128
+ hyperthreading/SMT/sibling cores on Linux via the `sysfs` pseudo filesystem.
129
+ It should be compatible with most modern Linux distributions as long as `sysfs` is mounted at `/sysfs`.
130
+ email:
131
+ - jhoblitt@cpan.org
132
+ executables:
133
+ - hyperctl
134
+ extensions: []
135
+ extra_rdoc_files: []
136
+ files:
137
+ - ".gitignore"
138
+ - ".travis.yml"
139
+ - Gemfile
140
+ - README.md
141
+ - Rakefile
142
+ - bin/hyperctl
143
+ - hyperctl.gemspec
144
+ - lib/hyperctl.rb
145
+ - lib/hyperctl/sysfs.rb
146
+ - lib/hyperctl/version.rb
147
+ - spec/hyperctl_spec.rb
148
+ - spec/hyperctl_sysfs_spec.rb
149
+ - spec/spec_helper.rb
150
+ homepage: https://github.com/jhoblitt/hyperctl
151
+ licenses:
152
+ - Apache 2.0
153
+ metadata: {}
154
+ post_install_message:
155
+ rdoc_options: []
156
+ require_paths:
157
+ - lib
158
+ required_ruby_version: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: 1.8.7
163
+ required_rubygems_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ requirements: []
169
+ rubyforge_project:
170
+ rubygems_version: 2.4.4
171
+ signing_key:
172
+ specification_version: 4
173
+ summary: A utility for enabling/disabling hyperthreading
174
+ test_files:
175
+ - spec/hyperctl_spec.rb
176
+ - spec/hyperctl_sysfs_spec.rb
177
+ - spec/spec_helper.rb
178
+ has_rdoc: