diff options
Diffstat (limited to 'scripts/tracing/dma-api/smmu.py')
-rw-r--r-- | scripts/tracing/dma-api/smmu.py | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/scripts/tracing/dma-api/smmu.py b/scripts/tracing/dma-api/smmu.py new file mode 100644 index 000000000000..0fbadff7119f --- /dev/null +++ b/scripts/tracing/dma-api/smmu.py @@ -0,0 +1,202 @@ +"""Low-level memory management tracking""" + +VERBOSITY = 0 # TODO: use logging + +class Bitmap(object): + """Just a raw bitmap for reserving the pages""" + def __init__(self, size, verbosity): + self._size = size + self._bits = 0 + self._verbosity = verbosity + self._bits_allocd = 0 + + def _mask(self, offset, length): + """length amount of 1's, shifted left by offset""" + assert offset >= 0, "offset < 0" + assert offset < self._size, "offset 0x%d >= size %s" % (offset, self._size) + bitstring = (1 << length) - 1 + return bitstring << offset + + def _indices(self, offset, length): + """Bit numbers starting from offset""" + return range(offset, offset + length) + + def alloc(self, offset, length): + """Reserve the bits, warn if verbose and some set already""" + mask = self._mask(offset, length) + if (self._bits & mask) != 0 and self._verbosity >= 1: + print "warning: duplicate allocation" + self._bits |= mask + self._bits_allocd += length + return self._indices(offset, length) + + def free(self, offset, length): + """Free the bits, warn if verbose and some not set yet""" + mask = self._mask(offset, length) + if (self._bits & mask) != mask and self._verbosity >= 1: + print "warning: freeing freed memory, mapbits %x" % (self._bits & mask) + self._bits &= ~mask + self._bits_allocd -= length + return self._indices(offset, length) + + def contains(self, offset, length): + """Are some bits in the given range set?""" + mask = self._mask(offset, length) + return self._bits & mask + + def __repr__(self): + return "<bitmap, %d/%d allocd>" % (self._bits_allocd, self._size) + +class Memory(object): + """Store and handle raw bitmaps, check mapping collisions between devices""" + PAGESHIFT = 12 + PAGESIZE = 1 << PAGESHIFT + PAGEMASK = PAGESIZE - 1 + + def __init__(self, addr, size, asid): + """addr: lowest possible address, size: bytes, asid: arbitrary id""" + assert (addr & self.PAGEMASK) == 0, addr + assert (size & self.PAGEMASK) == 0, size + + self._addr = addr + self._size = size + self._end = addr + size + self._asid = asid + self._bitmap = Bitmap(size >> self.PAGESHIFT, VERBOSITY) + self._devmaps = {} + + if VERBOSITY >= 1: + print "memory at %08x-%08x" % (addr, addr + size) + + def to_bit(self, addr): + """Address to bitmap position""" + return addr >> self.PAGESHIFT + + def alloc(self, dev, addr, size): + """Allocate (map) for the given device, verify things""" + if addr >= self._end: + if VERBOSITY >= 1: + print "warning: %s mapping beyond bitmap: %08x" % (dev, addr) + return [] + + if (addr & self.PAGEMASK) != 0: + if VERBOSITY >= 1: + print "warning: alloc not aligned at 0x%x, size %d (new addr 0x%x, size %d)" % ( + addr, size, addr & ~self.PAGEMASK, size + (addr & self.PAGEMASK)) + addr &= ~self.PAGEMASK + size += addr & self.PAGEMASK + + if size < self.PAGESIZE: + size = self.PAGESIZE + + for user, bmp in self._devmaps.iteritems(): + if bmp.contains(self.to_bit(addr - self._addr), self.to_bit(size)): + if VERBOSITY >= 1: + print "warning: %s mapping [0x%x,0x%x) already used by %s" % ( + dev, addr, addr + size, user) + + devmap = self._devmaps.setdefault(dev, Bitmap(self._bitmap._size, 0)) + + self._alloc(devmap, addr, size) + bits = self._alloc(self._bitmap, addr, size) + return bits + + def _alloc(self, bitmap, addr, size): + """Allocate from an internal bitmap""" + return bitmap.alloc(self.to_bit(addr - self._addr), self.to_bit(size)) + + def free(self, dev, addr, size): + """Free (unmap) for the given device, verify things""" + if (addr & self.PAGEMASK) != 0: + if VERBOSITY >= 1: + print "warning: free not aligned at 0x%x, size %d (new addr 0x%x, size %d)" % ( + addr, size, addr & ~self.PAGEMASK, size + (addr & self.PAGEMASK)) + addr &= ~self.PAGEMASK + size += addr & self.PAGEMASK + + if size < self.PAGESIZE: + size = self.PAGESIZE + + devmap = self._devmaps.setdefault(dev, Bitmap(self._bitmap._size, 0)) + + owners = [] + for user, bmp in self._devmaps.iteritems(): + if bmp.contains(self.to_bit(addr - self._addr), self.to_bit(size)): + owners.append((user, bmp)) + + if len(owners) == 0: + if VERBOSITY >= 1: + print "warning: %s freeing 0x%x that nobody owns" % (dev, addr) + elif len(owners) == 1: + if owners[0][0] != dev and VERBOSITY >= 2: + print "note: %s freeing 0x%x allocd by %s" % ( + dev, addr, owners[0][0]) + devmap = owners[0][1] + + self._free(devmap, addr, size) + bits = self._free(self._bitmap, addr, size) + return bits + + def _free(self, bitmap, addr, size): + """Free from an internal bitmap""" + return bitmap.free(self.to_bit(addr - self._addr), self.to_bit(size)) + + +class Device(object): + """Keep track of allocations per device/process + + This needs more tricky work for tracking inter-process maps/unmaps :( + """ + + def __init__(self, name, mem): + self._name = name + self._mem = mem + self._max_alloc = 0 + self._cur_alloc = 0 + self._alloc_history = [] + self._addresses = [] + + def alloc(self, addr, size): + pages = self._mem.alloc(self, addr, size) + if pages is not False: + self._cur_alloc += size + self._max_alloc = max(self._max_alloc, self._cur_alloc) + self._alloc_history.append(self._cur_alloc) + if addr in self._addresses: + if VERBOSITY >= 1: + print "warning: %s allocing dupe address %x %s" % (self._name, addr, len([x for x in self._addresses if x == addr])) + self._addresses.append(addr) + return pages + + def free(self, addr, size): + pages = self._mem.free(self, addr, size) + self._cur_alloc -= size + if addr in self._addresses: + self._addresses.remove(addr) + else: + if VERBOSITY >= 1: + print "warning: %s freeing unallocated %x" % (self._name, addr) + return pages + + def history_at(self, i): + return self._alloc_history[i] + + @property + def name(self): + return self._name + + def __str__(self): + return self.name + + def __repr__(self): + return "<dev: %s>" % self.name + + @property + def max_allocated(self): + return self._max_alloc + + +class AsidSpace(object): + # TODO: don't pre-grep by archdata but put devices' mem maps here. + pass + |