11# -*- coding: utf-8 -*-
22# vim: ts=4 sw=4 tw=100 et ai si
33#
4- # Copyright (C) 2020-2025 Intel Corporation
4+ # Copyright (C) 2020-2026 Intel Corporation
55# SPDX-License-Identifier: BSD-3-Clause
66#
77# Authors: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
1414
1515from __future__ import annotations # Remove when switching to Python 3.10+.
1616
17+ import os
1718import typing
1819import pprint
1920from pathlib import Path
@@ -67,14 +68,14 @@ class MSR(_SimpleMSR.SimpleMSR):
6768
6869 1. Multi-CPU I/O.
6970 - 'read()' - read an MSR.
70- - 'read_bits()' - read an MSR bits range.
71- - 'write()' - write to the an MSR.
72- - 'write_bits()' - write MSR bits range.
71+ - 'read_bits()' - read an MSR bits range.
72+ - 'write()' - write to an MSR.
73+ - 'write_bits()' - write MSR bits range.
7374 2. Single-CPU I/O.
7475 - 'read_cpu()' - read an MSR.
75- - 'read_cpu_bits()' - read an MSR bits range.
76- - 'write_cpu()' - write to the an MSR.
77- - 'write_cpu_bits()' - write MSR bits range.
76+ - 'read_cpu_bits()' - read an MSR bits range.
77+ - 'write_cpu()' - write to an MSR.
78+ - 'write_cpu_bits()' - write MSR bits range.
7879 3. Transactions support.
7980 - 'start_transaction()' - start a transaction.
8081 - 'flush_transaction()' - flush the transaction buffer.
@@ -101,7 +102,7 @@ def __init__(self,
101102 process manager will be used.
102103 enable_cache: If True, enable caching of MSR values. The first read fetches from
103104 hardware, subsequent reads return the cached value. Writes update the
104- cache and and propagate to hardware immediately (write-through policy). If
105+ cache and propagate to hardware immediately (write-through policy). If
105106 False, caching is disabled, and every read/write operation accesses the
106107 hardware directly.
107108 """
@@ -117,12 +118,10 @@ def __init__(self,
117118 # make sure writes go to all CPUs, not just one CPU in the scope.
118119 self ._enable_scope = False
119120
121+ # The write-through per-CPU MSR values cache.
120122 self ._cache = _PerCPUCache .PerCPUCache (cpuinfo , enable_cache = self ._enable_cache ,
121123 enable_scope = self ._enable_scope )
122- self ._cache ._cpuinfo = cpuinfo
123124
124- # The write-through per-CPU MSR values cache.
125- self ._cache = _PerCPUCache .PerCPUCache (cpuinfo , enable_cache = self ._enable_cache )
126125 # The transaction buffer. This is a dictionary of dictionaries, where the first key is the
127126 # CPU number, and the second key is the MSR address. The value is a dictionary with
128127 # transaction value and additional information.
@@ -139,7 +138,7 @@ def close(self):
139138
140139 super ().close ()
141140
142- def _add_for_transation (self ,
141+ def _add_for_transaction (self ,
143142 regaddr : int ,
144143 regval : int ,
145144 cpu : int ,
@@ -157,9 +156,6 @@ def _add_for_transation(self,
157156 iosname: The I/O scope name associated with the MSR.
158157 """
159158
160- if not self ._enable_cache :
161- raise Error ("Transactions support requires caching to be enabled" )
162-
163159 if cpu not in self ._transaction_buffer :
164160 self ._transaction_buffer [cpu ] = {}
165161
@@ -171,7 +167,7 @@ def _add_for_transation(self,
171167 f" old: { tinfo ['iosname' ]} , new: { iosname } " )
172168 if "verify" in tinfo and tinfo ["verify" ] != verify :
173169 raise Error (f"BUG: Inconsistent verification flag value for MSR { regaddr :#x} :\n "
174- f" old: { tinfo ['iosname ' ]} , new: { iosname } " )
170+ f" old: { tinfo ['verify ' ]} , new: { verify } " )
175171 else :
176172 tinfo = self ._transaction_buffer [cpu ][regaddr ] = {}
177173
@@ -186,7 +182,7 @@ def start_transaction(self):
186182 When a transaction is active, all writes to MSRs are buffered and only written to hardware
187183 upon calling 'commit_transaction()' or 'flush_transaction()'. Writes to the same MSR are
188184 merged into a single operation to minimize I/O overhead. Transactions do not provide
189- atomicity or rollback; they are intended solely for optimizing I/O by batching and merging
185+ atomicity or rollback. They are intended solely for optimizing I/O by batching and merging
190186 writes.
191187 """
192188
@@ -226,26 +222,25 @@ def _verify(self, regaddr: int, regval: int, cpus: list[int], iosname: ScopeName
226222 f"{ self ._pman .hostmsg } :\n Wrote '{ regval :#x} ', read back "
227223 f"'{ new_val :#x} '" , cpu = cpu , expected = regval , actual = new_val )
228224
229- def _transaction_write (self ):
230- """Write the contents of the transaction buffer to MSRs ."""
225+ def _transaction_write_local (self ):
226+ """Write MSR transactions on a local host ."""
231227
232228 for cpu , cpus_info in self ._transaction_buffer .items ():
233- # Write all the dirty data.
234229 path = Path (f"/dev/cpu/{ cpu } /msr" )
235- with self ._pman .open (path , "r+b" ) as fobj :
230+ fd = os .open (path , os .O_RDWR )
231+ try :
236232 for regaddr , regval_info in cpus_info .items ():
237233 regval = regval_info ["regval" ]
238234 try :
239- fobj .seek (regaddr )
240235 regval_bytes = regval .to_bytes (self .regbytes , byteorder = _CPU_BYTEORDER )
241- fobj .write (regval_bytes )
242- fobj .flush ()
243- _LOG .debug ("CPU%d: Commit MSR 0x%x: Wrote 0x%x%s" ,
244- cpu , regaddr , regval , self ._pman .hostmsg )
245- except Error as err :
236+ os .pwrite (fd , regval_bytes , regaddr )
237+ except OSError as err :
246238 raise Error (f"Failed to write '{ regval :#x} ' to MSR '{ regaddr :#x} ' of CPU "
247- f"{ cpu } { self ._pman .hostmsg } (file '{ path } '):\n "
248- f"{ err .indent (2 )} " ) from err
239+ f"{ cpu } { self ._pman .hostmsg } (file '{ path } '): { err } " ) from err
240+ _LOG .debug ("CPU%d: Commit MSR 0x%x: Wrote 0x%x%s" ,
241+ cpu , regaddr , regval , self ._pman .hostmsg )
242+ finally :
243+ os .close (fd )
249244
250245 def _transaction_write_remote (self ):
251246 """
@@ -265,20 +260,42 @@ def _transaction_write_remote(self):
265260 transaction_buffer_str = transaction_buffer_str .replace ("'" , "\" " )
266261
267262 cmd = f"""{ python_path } -c '
263+ import os
268264transaction_buffer = { transaction_buffer_str }
269265for cpu, cpus_info in transaction_buffer.items():
270266 path = "/dev/cpu/%d/msr" % cpu
271- with open(path, "r+b") as fobj:
267+ fd = os.open(path, os.O_RDWR)
268+ try:
272269 for regaddr, regval_info in cpus_info.items():
273270 regval = regval_info["regval"]
274- fobj.seek(regaddr)
275271 regval_bytes = regval.to_bytes({ self .regbytes } , byteorder="{ _CPU_BYTEORDER } ")
276- fobj.write(regval_bytes)
277- fobj.flush()
272+ os.pwrite(fd, regval_bytes, regaddr)
273+ finally:
274+ os.close(fd)
278275'"""
279276
280277 self ._pman .run_verify (cmd )
281278
279+ def _transaction_write_emulation (self ):
280+ """Write MSR transactions on an emulated host."""
281+
282+ for cpu , cpus_info in self ._transaction_buffer .items ():
283+ path = Path (f"/dev/cpu/{ cpu } /msr" )
284+ with self ._pman .open (path , "r+b" ) as fobj :
285+ for regaddr , regval_info in cpus_info .items ():
286+ regval = regval_info ["regval" ]
287+ try :
288+ fobj .seek (regaddr )
289+ regval_bytes = regval .to_bytes (self .regbytes , byteorder = _CPU_BYTEORDER )
290+ fobj .write (regval_bytes )
291+ fobj .flush ()
292+ except Error as err :
293+ raise Error (f"Failed to write '{ regval :#x} ' to MSR '{ regaddr :#x} ' of CPU "
294+ f"{ cpu } { self ._pman .hostmsg } (file '{ path } '):\n "
295+ f"{ err .indent (2 )} " ) from err
296+ _LOG .debug ("CPU%d: Commit MSR 0x%x: Wrote 0x%x%s" ,
297+ cpu , regaddr , regval , self ._pman .hostmsg )
298+
282299 def flush_transaction (self ) -> bool :
283300 """
284301 Flush the transaction buffer and write all buffered data to the MSRs.
@@ -303,8 +320,10 @@ def flush_transaction(self) -> bool:
303320
304321 if self ._pman .is_remote :
305322 self ._transaction_write_remote ()
323+ elif isinstance (self ._pman , EmulProcessManager .EmulProcessManager ):
324+ self ._transaction_write_emulation ()
306325 else :
307- self ._transaction_write ()
326+ self ._transaction_write_local ()
308327
309328 # Form a temporary dictionary for verifying the contents of the MSRs written to by the
310329 # transaction.
@@ -336,7 +355,7 @@ def commit_transaction(self):
336355 Commit the current MSR transaction by flushing all buffered data to the MSRs and closing the
337356 transaction.
338357
339- This method does not provide atomicity guarantees; it is intended as an optimization to
358+ This method does not provide atomicity guarantees. It is intended as an optimization to
340359 reduce the number of MSR I/O operations.
341360 """
342361
@@ -374,10 +393,9 @@ def _read_remote(self,
374393 yield from super ()._cpus_read_remote (regaddr , cpus )
375394 return
376395
377-
378396 # CPU numbers to read the MSR for (subset of 'cpus').
379397 do_read = []
380- # CPU numbers the MSR should not be read for. Instead, the MSR values for these CPU numbres
398+ # CPU numbers the MSR should not be read for. Instead, the MSR values for these CPU numbers
381399 # are available from the cache, or will be available from the cache when a sibling CPU from
382400 # 'do_read' is read.
383401 dont_read = set ()
@@ -394,8 +412,7 @@ def _read_remote(self,
394412 do_read .append (cpu )
395413 continue
396414
397- # Read the MSR only for 'iosname' sibling CPU, because MSR value should be the same
398- # for the siblings.
415+ # Read only one CPU per 'iosname' scope, because sibling CPUs share the same MSR value.
399416 for sibling in self ._cpuinfo .get_cpu_siblings (cpu , iosname ):
400417 if sibling == cpu :
401418 do_read .append (sibling )
@@ -457,7 +474,7 @@ def read(self,
457474 cpus = self ._cpuinfo .normalize_cpus (cpus )
458475 yield from self ._read (regaddr , cpus , iosname )
459476
460- def read_cpu (self , regaddr , cpu , iosname = "CPU" ):
477+ def read_cpu (self , regaddr : int , cpu : int , iosname : ScopeNameType = "CPU" ) -> int :
461478 """
462479 Read an MSR value from a specific CPU.
463480
@@ -470,11 +487,10 @@ def read_cpu(self, regaddr, cpu, iosname="CPU"):
470487 The value read from the specified MSR on the given CPU.
471488 """
472489
473- regval = None
474490 for _ , regval in self .read (regaddr , cpus = (cpu ,), iosname = iosname ):
475- pass
491+ return regval
476492
477- return regval
493+ raise Error ( f"Failed to read MSR 0x { regaddr :x } from CPU { cpu } { self . _pman . hostmsg } " )
478494
479495 def read_bits (self ,
480496 regaddr : int ,
@@ -487,8 +503,8 @@ def read_bits(self,
487503 Args:
488504 regaddr: Address of the MSR to read bits from.
489505 bits: A tuple or list of two integers (msb, lsb) specifying the bit range to extract
490- from the MSR; msb is the most significant bit and lsb is the least significant
491- bit.
506+ from the MSR, where msb is the most significant bit and lsb is the least
507+ significant bit.
492508 cpus: CPU numbers to read the MSR from. Special value 'all' means "all CPUs".
493509 iosname: Scope name for the MSR (e.g. "package", "core"). This is used for
494510 optimizing the read operation by skipping unnecessary reads of sibling CPUs.
@@ -500,10 +516,10 @@ def read_bits(self,
500516 """
501517
502518 for cpu , regval in self .read (regaddr , cpus , iosname = iosname ):
519+ val = self .get_bits (regval , bits )
503520 _LOG .debug ("CPU%d: MSR 0x%x: Bits %s: Read 0x%x%s" , cpu , regaddr ,
504- ":" .join ([str (bit ) for bit in bits ]), self .get_bits (regval , bits ),
505- self ._pman .hostmsg )
506- yield (cpu , self .get_bits (regval , bits ))
521+ ":" .join ([str (bit ) for bit in bits ]), val , self ._pman .hostmsg )
522+ yield cpu , val
507523
508524 def read_cpu_bits (self ,
509525 regaddr : int ,
@@ -516,8 +532,8 @@ def read_cpu_bits(self,
516532 Args:
517533 regaddr: Address of the MSR to read from.
518534 bits: A tuple or list of two integers (msb, lsb) specifying the bit range to extract
519- from the MSR; msb is the most significant bit and lsb is the least significant
520- bit.
535+ from the MSR, where msb is the most significant bit and lsb is the least
536+ significant bit.
521537 cpu: CPU number to read the MSR from.
522538 iosname: Scope name for the MSR (e.g. "package", "core").
523539
@@ -578,8 +594,7 @@ def _write_remote(self,
578594 do_write .append (cpu )
579595 continue
580596
581- # Write the MSR only on 'iosname' sibling CPU, because MSR value should be the same
582- # for the siblings.
597+ # Write only one CPU per 'iosname' scope, because sibling CPUs share the same MSR value.
583598 for sibling in self ._cpuinfo .get_cpu_siblings (cpu , iosname ):
584599 if sibling == cpu :
585600 do_write .append (sibling )
@@ -627,12 +642,11 @@ def _write(self,
627642 if not self ._in_transaction :
628643 super ().cpu_write (regaddr , regval , cpu )
629644 else :
630- self ._add_for_transation (regaddr , regval , cpu , verify , iosname )
645+ self ._add_for_transaction (regaddr , regval , cpu , verify , iosname )
631646
632- # Note, below 'add()' call is scope-aware. It will cache 'regval' not only for CPU
633- # number 'cpu', but also for all the 'iosname' siblings. For example, if 'iosname' is
634- # "package", 'regval' will be cached for all CPUs in the package that contains CPU
635- # number 'cpu'.
647+ # The '_cache.add()' call below is scope-aware: it caches 'regval' not only for CPU
648+ # 'cpu', but also for all its 'iosname' siblings. For example, if 'iosname' is
649+ # "package", 'regval' is cached for all CPUs in the package containing CPU 'cpu'.
636650 self ._cache .add (regaddr , cpu , regval , sname = iosname )
637651
638652 # In case of an ongoing transaction, skip the verification, it'll be done at the end of the
@@ -702,8 +716,8 @@ def write_bits(self,
702716
703717 Args:
704718 regaddr: The address of the MSR to write to.
705- bits: A tuple or list of two integers (msb, lsb) specifying the bit range to to write
706- to; msb is the most significant bit and lsb is the least significant bit.
719+ bits: A tuple or list of two integers (msb, lsb) specifying the bit range to write to;
720+ msb is the most significant bit and lsb is the least significant bit.
707721 val: The value to write to the specified bits range.
708722 cpus: CPU numbers to write the MSR on. Special value 'all' means "all CPUs".
709723 iosname: I/O scope name for the MSR address (e.g., "package", "core"). Used for
@@ -755,8 +769,8 @@ def write_cpu_bits(self,
755769
756770 Args:
757771 regaddr: The address of the MSR to write to.
758- bits: A tuple or list of two integers (msb, lsb) specifying the bit range to to write
759- to; msb is the most significant bit and lsb is the least significant bit.
772+ bits: A tuple or list of two integers (msb, lsb) specifying the bit range to write to;
773+ msb is the most significant bit and lsb is the least significant bit.
760774 val: The value to write to the specified bits range.
761775 cpu: CPU number to write the MSR on.
762776 iosname: I/O scope name for the MSR address (e.g., "package", "core"). Used for
0 commit comments