hyperctl 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: