| # -*- python -*- |
| # -*- coding: utf-8 -*- |
| from tuna import tuna, gui |
| import procfs |
| from gi.repository import Gdk |
| from gi.repository import Gtk |
| from gi.repository import GObject |
| import os |
| from functools import reduce |
| import tuna.new_eth as ethtool |
| import tuna.tuna_sched as tuna_sched |
| |
| import gi |
| gi.require_version("Gtk", "3.0") |
| |
| |
| class irq_druid: |
| |
| def __init__(self, irqs, ps, irq, gladefile): |
| self.irqs = irqs |
| self.ps = ps |
| self.irq = irq |
| self.window = Gtk.Builder() |
| self.window.add_objects_from_file(gladefile, ("set_irq_attributes", "tuna")) |
| #self.window = Gtk.glade.XML(gladefile, "set_irq_attributes", "tuna") |
| self.dialog = self.window.get_object("set_irq_attributes") |
| pixbuf = self.dialog.render_icon(Gtk.STOCK_PREFERENCES, |
| Gtk.IconSize.SMALL_TOOLBAR) |
| self.dialog.set_icon(pixbuf) |
| event_handlers = { |
| "on_irq_affinity_text_changed": self.on_irq_affinity_text_changed, |
| "on_sched_policy_combo_changed": self.on_sched_policy_combo_changed} |
| # self.window.signal_autoconnect(event_handlers) |
| self.window.connect_signals(event_handlers) |
| |
| self.sched_pri = self.window.get_object("irq_pri_spinbutton") |
| self.sched_pri.set_range(0, 99) |
| self.sched_policy = self.window.get_object("irq_policy_combobox") |
| self.affinity = self.window.get_object("irq_affinity_text") |
| text = self.window.get_object("irq_text") |
| |
| users = tuna.get_irq_users(irqs, irq) |
| self.affinity_text = tuna.get_irq_affinity_text(irqs, irq) |
| |
| irq_re = tuna.threaded_irq_re(irq) |
| pids = self.ps.find_by_regex(irq_re) |
| if pids: |
| pid = pids[0] |
| prio = int(ps[pid]["stat"]["rt_priority"]) |
| self.create_policy_model(self.sched_policy) |
| self.sched_policy.set_active(os.sched_getscheduler(pid)) |
| self.sched_pri.set_value(prio) |
| text.set_markup( |
| "IRQ <b>%u</b> (PID <b>%u</b>), pri <b>%u</b>, aff <b>%s</b>, <tt><b>%s</b></tt>" % |
| (irq, pid, prio, self.affinity_text, ",".join(users))) |
| else: |
| self.sched_pri.set_sensitive(False) |
| self.sched_policy.set_sensitive(False) |
| text.set_markup( |
| "IRQ <b>%u</b>, aff <b>%s</b>, <tt><b>%s</b></tt>" % |
| (irq, self.affinity_text, ",".join(users))) |
| self.affinity.set_text(self.affinity_text) |
| |
| def create_policy_model(self, policy): |
| (COL_TEXT, COL_SCHED) = list(range(2)) |
| list_store = Gtk.ListStore(GObject.TYPE_STRING, |
| GObject.TYPE_UINT) |
| renderer = Gtk.CellRendererText() |
| policy.pack_start(renderer, True) |
| policy.add_attribute(renderer, "text", COL_TEXT) |
| for pol in range(4): |
| row = list_store.append() |
| list_store.set(row, COL_TEXT, tuna_sched.sched_str(pol), |
| COL_SCHED, pol) |
| policy.set_model(list_store) |
| |
| def on_sched_policy_combo_changed(self, button): |
| new_policy = self.sched_policy.get_active() |
| if new_policy in (os.SCHED_FIFO, os.SCHED_RR): |
| can_change_pri = True |
| else: |
| can_change_pri = False |
| self.sched_pri.set_sensitive(can_change_pri) |
| |
| def on_irq_affinity_text_changed(self, button): |
| gui.on_affinity_text_changed(self) |
| |
| def run(self): |
| changed = False |
| if self.dialog.run() == Gtk.ResponseType.OK: |
| new_policy = self.sched_policy.get_active() |
| new_prio = int(self.sched_pri.get_value()) |
| new_affinity = self.affinity.get_text() |
| irq_re = tuna.threaded_irq_re(self.irq) |
| pids = self.ps.find_by_regex(irq_re) |
| if pids: |
| if gui.thread_set_attributes(self.ps[pids[0]], |
| new_policy, |
| new_prio, |
| new_affinity, |
| self.irqs.nr_cpus): |
| changed = True |
| try: |
| new_affinity = [int(a) for a in new_affinity.split(",")] |
| except: |
| try: |
| new_affinity = tuna.cpustring_to_list(new_affinity) |
| except: |
| new_affinity = procfs.bitmasklist(new_affinity, |
| self.irqs.nr_cpus) |
| |
| new_affinity.sort() |
| |
| curr_affinity = self.irqs[self.irq]["affinity"] |
| if curr_affinity != new_affinity: |
| tuna.set_irq_affinity( |
| self.irq, procfs.hexbitmask(new_affinity, |
| self.irqs.nr_cpus)) |
| changed = True |
| |
| self.dialog.destroy() |
| return changed |
| |
| |
| class irqview: |
| |
| nr_columns = 7 |
| (COL_NUM, COL_PID, COL_POL, COL_PRI, |
| COL_AFF, COL_EVENTS, COL_USERS) = list(range(nr_columns)) |
| columns = (gui.list_store_column(_("IRQ")), |
| gui.list_store_column(_("PID"), GObject.TYPE_INT), |
| gui.list_store_column(_("Policy"), GObject.TYPE_STRING), |
| gui.list_store_column(_("Priority"), GObject.TYPE_INT), |
| gui.list_store_column(_("Affinity"), GObject.TYPE_STRING), |
| gui.list_store_column(_("Events")), |
| gui.list_store_column(_("Users"), GObject.TYPE_STRING)) |
| |
| def __init__(self, treeview, irqs, ps, cpus_filtered, gladefile): |
| |
| self.is_root = os.getuid() == 0 |
| self.irqs = irqs |
| self.ps = ps |
| self.treeview = treeview |
| self.gladefile = gladefile |
| self.has_threaded_irqs = tuna.has_threaded_irqs(ps) |
| if not self.has_threaded_irqs: |
| self.nr_columns = 4 |
| (self.COL_NUM, |
| self.COL_AFF, |
| self.COL_EVENTS, |
| self.COL_USERS) = list(range(self.nr_columns)) |
| self.columns = (gui.list_store_column(_("IRQ")), |
| gui.list_store_column( |
| _("Affinity"), GObject.TYPE_STRING), |
| gui.list_store_column(_("Events")), |
| gui.list_store_column(_("Users"), GObject.TYPE_STRING)) |
| |
| self.list_store = Gtk.ListStore( |
| *gui.generate_list_store_columns_with_attr(self.columns)) |
| |
| # Allow selecting multiple rows |
| selection = treeview.get_selection() |
| selection.set_mode(Gtk.SelectionMode.MULTIPLE) |
| |
| # Allow enable drag and drop of rows |
| self.treeview.enable_model_drag_source( |
| Gdk.ModifierType.BUTTON1_MASK, |
| gui.DND_TARGETS, |
| Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE) |
| self.treeview.connect("drag_data_get", self.on_drag_data_get_data) |
| self.renderer = Gtk.CellRendererText() |
| |
| for col in range(self.nr_columns): |
| column = Gtk.TreeViewColumn(self.columns[col].name, |
| self.renderer, text=col) |
| column.set_sort_column_id(col) |
| column.add_attribute(self.renderer, "weight", |
| col + self.nr_columns) |
| self.treeview.append_column(column) |
| |
| self.cpus_filtered = cpus_filtered |
| self.refreshing = True |
| |
| self.treeview.set_model(self.list_store) |
| |
| def foreach_selected_cb(self, model, path, iter, irq_list): |
| irq = model.get_value(iter, self.COL_NUM) |
| irq_list.append(str(irq)) |
| |
| def on_drag_data_get_data(self, treeview, context, |
| selection, target_id, etime): |
| treeselection = treeview.get_selection() |
| irq_list = [] |
| treeselection.selected_foreach(self.foreach_selected_cb, irq_list) |
| selection.set(selection.target, 8, "irq:" + ",".join(irq_list)) |
| |
| def set_irq_columns(self, iter, irq, irq_info, nics): |
| new_value = [None] * self.nr_columns |
| users = tuna.get_irq_users(self.irqs, irq, nics) |
| if self.has_threaded_irqs: |
| irq_re = tuna.threaded_irq_re(irq) |
| pids = self.ps.find_by_regex(irq_re) |
| if pids: |
| pid = pids[0] |
| prio = int(self.ps[pid]["stat"]["rt_priority"]) |
| sched = tuna_sched.sched_str(os.sched_getscheduler(pid))[6:] |
| else: |
| sched = "" |
| pid = -1 |
| prio = -1 |
| new_value[self.COL_PID] = pid |
| new_value[self.COL_POL] = sched |
| new_value[self.COL_PRI] = prio |
| |
| new_value[self.COL_NUM] = irq |
| new_value[self.COL_AFF] = tuna.get_irq_affinity_text(self.irqs, irq) |
| new_value[self.COL_EVENTS] = reduce( |
| lambda a, b: a + b, irq_info["cpu"]) |
| new_value[self.COL_USERS] = ",".join(users) |
| |
| gui.set_store_columns(self.list_store, iter, new_value) |
| |
| def show(self): |
| new_irqs = [] |
| for sirq in list(self.irqs.keys()): |
| try: |
| new_irqs.append(int(sirq)) |
| except: |
| continue |
| |
| nics = ethtool.get_active_devices() |
| |
| row = self.list_store.get_iter_first() |
| while row: |
| irq = self.list_store.get_value(row, self.COL_NUM) |
| # IRQ was unregistered? I.e. driver unloaded? |
| if irq not in self.irqs: |
| if self.list_store.remove(row): |
| # removed and row now its the next one |
| continue |
| # Was the last one |
| break |
| if tuna.irq_filtered(irq, self.irqs, self.cpus_filtered, |
| self.is_root): |
| new_irqs.remove(irq) |
| if self.list_store.remove(row): |
| # removed and row now its the next one |
| continue |
| # Was the last one |
| break |
| try: |
| new_irqs.remove(irq) |
| irq_info = self.irqs[irq] |
| self.set_irq_columns(row, irq, irq_info, nics) |
| except: |
| if self.list_store.remove(row): |
| # removed and row now its the next one |
| continue |
| # Was the last one |
| break |
| |
| row = self.list_store.iter_next(row) |
| |
| new_irqs.sort() |
| for irq in new_irqs: |
| if tuna.irq_filtered(irq, self.irqs, self.cpus_filtered, |
| self.is_root): |
| continue |
| row = self.list_store.append() |
| irq_info = self.irqs[irq] |
| try: |
| self.set_irq_columns(row, irq, irq_info, nics) |
| except: |
| self.list_store.remove(row) |
| |
| self.treeview.show_all() |
| |
| def refresh(self): |
| if not self.refreshing: |
| return |
| self.irqs.reload() |
| self.show() |
| |
| def refresh_toggle(self, unused): |
| self.refreshing = not self.refreshing |
| |
| def edit_attributes(self, a): |
| ret = self.treeview.get_path_at_pos(self.last_x, self.last_y) |
| if not ret: |
| return |
| path, col, xpos, ypos = ret |
| if not path: |
| return |
| row = self.list_store.get_iter(path) |
| irq = self.list_store.get_value(row, self.COL_NUM) |
| if irq not in self.irqs: |
| return |
| |
| dialog = irq_druid(self.irqs, self.ps, irq, self.gladefile) |
| if dialog.run(): |
| self.refresh() |
| |
| def on_irqlist_button_press_event(self, treeview, event): |
| if event.type != Gdk.EventType.BUTTON_PRESS or event.button != 3: |
| return |
| |
| self.last_x = int(event.x) |
| self.last_y = int(event.y) |
| |
| menu = Gtk.Menu() |
| |
| setattr = Gtk.MenuItem(_("_Set IRQ attributes"), use_underline = True) |
| if self.refreshing: |
| refresh = Gtk.MenuItem(_("Sto_p refreshing the IRQ list"), use_underline = True) |
| else: |
| refresh = Gtk.MenuItem(_("_Refresh the IRQ list"), use_underline = True) |
| |
| menu.add(setattr) |
| menu.add(refresh) |
| |
| setattr.connect_object('activate', self.edit_attributes, event) |
| refresh.connect_object('activate', self.refresh_toggle, event) |
| |
| setattr.show() |
| refresh.show() |
| |
| menu.popup(None, None, None, event, event.button, event.time) |
| |
| def toggle_mask_cpu(self, cpu, enabled): |
| if not enabled: |
| if cpu not in self.cpus_filtered: |
| self.cpus_filtered.append(cpu) |
| self.show() |
| else: |
| if cpu in self.cpus_filtered: |
| self.cpus_filtered.remove(cpu) |
| self.show() |