#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/slab.h>
-#include <linux/completion.h>
#include <linux/delay.h>
+#include <linux/kthread.h>
#include <linux/moduleparam.h>
#include <asm/atomic.h>
* but now we are much simpler because of the LDM.
*/
-static DECLARE_MUTEX(nodemgr_serialize);
+static DEFINE_MUTEX(nodemgr_serialize);
struct host_info {
struct hpsb_host *host;
struct list_head list;
- struct completion exited;
- struct semaphore reset_sem;
- int pid;
- char daemon_name[15];
- int kill_me;
+ struct task_struct *thread;
};
static int nodemgr_bus_match(struct device * dev, struct device_driver * drv);
static DEVICE_ATTR(bus_options,S_IRUGO,fw_show_ne_bus_options,NULL);
-/* tlabels_free, tlabels_allocations, tlabels_mask are read non-atomically
- * here, therefore displayed values may be occasionally wrong. */
-static ssize_t fw_show_ne_tlabels_free(struct device *dev, struct device_attribute *attr, char *buf)
+#ifdef HPSB_DEBUG_TLABELS
+static ssize_t fw_show_ne_tlabels_free(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct node_entry *ne = container_of(dev, struct node_entry, device);
- return sprintf(buf, "%d\n", 64 - bitmap_weight(ne->tpool->pool, 64));
-}
-static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL);
+ unsigned long flags;
+ unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map;
+ int tf;
+ spin_lock_irqsave(&hpsb_tlabel_lock, flags);
+ tf = 64 - bitmap_weight(tp, 64);
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
-static ssize_t fw_show_ne_tlabels_allocations(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct node_entry *ne = container_of(dev, struct node_entry, device);
- return sprintf(buf, "%u\n", ne->tpool->allocations);
+ return sprintf(buf, "%d\n", tf);
}
-static DEVICE_ATTR(tlabels_allocations,S_IRUGO,fw_show_ne_tlabels_allocations,NULL);
+static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL);
-static ssize_t fw_show_ne_tlabels_mask(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t fw_show_ne_tlabels_mask(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct node_entry *ne = container_of(dev, struct node_entry, device);
+ unsigned long flags;
+ unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map;
+ u64 tm;
+
+ spin_lock_irqsave(&hpsb_tlabel_lock, flags);
#if (BITS_PER_LONG <= 32)
- return sprintf(buf, "0x%08lx%08lx\n", ne->tpool->pool[0], ne->tpool->pool[1]);
+ tm = ((u64)tp[0] << 32) + tp[1];
#else
- return sprintf(buf, "0x%016lx\n", ne->tpool->pool[0]);
+ tm = tp[0];
#endif
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
+
+ return sprintf(buf, "0x%016llx\n", tm);
}
static DEVICE_ATTR(tlabels_mask, S_IRUGO, fw_show_ne_tlabels_mask, NULL);
+#endif /* HPSB_DEBUG_TLABELS */
static ssize_t fw_set_ignore_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
&dev_attr_ne_vendor_id,
&dev_attr_ne_nodeid,
&dev_attr_bus_options,
+#ifdef HPSB_DEBUG_TLABELS
&dev_attr_tlabels_free,
- &dev_attr_tlabels_allocations,
&dev_attr_tlabels_mask,
+#endif
};
if (!ne)
return NULL;
- ne->tpool = &host->tpool[nodeid & NODE_MASK];
-
ne->host = host;
ne->nodeid = nodeid;
ne->generation = generation;
}
+/* Caller needs to hold nodemgr_ud_class.subsys.rwsem as reader. */
static void nodemgr_suspend_ne(struct node_entry *ne)
{
struct class_device *cdev;
ne->in_limbo = 0;
device_remove_file(&ne->device, &dev_attr_ne_in_limbo);
+ down_read(&nodemgr_ud_class.subsys.rwsem);
down_read(&ne->device.bus->subsys.rwsem);
list_for_each_entry(cdev, &nodemgr_ud_class.children, node) {
ud = container_of(cdev, struct unit_directory, class_dev);
ud->device.driver->resume(&ud->device);
}
up_read(&ne->device.bus->subsys.rwsem);
+ up_read(&nodemgr_ud_class.subsys.rwsem);
HPSB_DEBUG("Node resumed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid);
}
+/* Caller needs to hold nodemgr_ud_class.subsys.rwsem as reader. */
static void nodemgr_update_pdrv(struct node_entry *ne)
{
struct unit_directory *ud;
struct hpsb_protocol_driver *pdrv;
- struct class *class = &nodemgr_ud_class;
struct class_device *cdev;
- down_read(&class->subsys.rwsem);
- list_for_each_entry(cdev, &class->children, node) {
+ list_for_each_entry(cdev, &nodemgr_ud_class.children, node) {
ud = container_of(cdev, struct unit_directory, class_dev);
if (ud->ne != ne || !ud->device.driver)
continue;
up_write(&ud->device.bus->subsys.rwsem);
}
}
- up_read(&class->subsys.rwsem);
}
}
+/* Caller needs to hold nodemgr_ud_class.subsys.rwsem as reader because the
+ * calls to nodemgr_update_pdrv() and nodemgr_suspend_ne() here require it. */
static void nodemgr_probe_ne(struct host_info *hi, struct node_entry *ne, int generation)
{
struct device *dev;
/* If we had a bus reset while we were scanning the bus, it is
* possible that we did not probe all nodes. In that case, we
* skip the clean up for now, since we could remove nodes that
- * were still on the bus. The bus reset increased hi->reset_sem,
- * so there's a bus scan pending which will do the clean up
- * eventually.
+ * were still on the bus. Another bus scan is pending which will
+ * do the clean up eventually.
*
* Now let's tell the bus to rescan our devices. This may seem
* like overhead, but the driver-model core will only scan a
{
struct host_info *hi = (struct host_info *)__hi;
struct hpsb_host *host = hi->host;
- int reset_cycles = 0;
-
- /* No userlevel access needed */
- daemonize(hi->daemon_name);
+ unsigned int g, generation = get_hpsb_generation(host) - 1;
+ int i, reset_cycles = 0;
/* Setup our device-model entries */
nodemgr_create_host_dev_files(host);
- /* Sit and wait for a signal to probe the nodes on the bus. This
- * happens when we get a bus reset. */
- while (1) {
- unsigned int generation = 0;
- int i;
+ for (;;) {
+ /* Sleep until next bus reset */
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (get_hpsb_generation(host) == generation)
+ schedule();
+ __set_current_state(TASK_RUNNING);
+
+ /* Thread may have been woken up to freeze or to exit */
+ if (try_to_freeze())
+ continue;
+ if (kthread_should_stop())
+ goto exit;
- if (down_interruptible(&hi->reset_sem) ||
- down_interruptible(&nodemgr_serialize)) {
+ if (mutex_lock_interruptible(&nodemgr_serialize)) {
if (try_to_freeze())
continue;
- printk("NodeMgr: received unexpected signal?!\n" );
- break;
- }
-
- if (hi->kill_me) {
- up(&nodemgr_serialize);
- break;
+ goto exit;
}
/* Pause for 1/4 second in 1/16 second intervals,
* to make sure things settle down. */
+ g = get_hpsb_generation(host);
for (i = 0; i < 4 ; i++) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (msleep_interruptible(63)) {
- up(&nodemgr_serialize);
- goto caught_signal;
- }
+ if (msleep_interruptible(63) || kthread_should_stop())
+ goto unlock_exit;
/* Now get the generation in which the node ID's we collect
* are valid. During the bus scan we will use this generation
/* If we get a reset before we are done waiting, then
* start the the waiting over again */
- while (!down_trylock(&hi->reset_sem))
- i = 0;
-
- /* Check the kill_me again */
- if (hi->kill_me) {
- up(&nodemgr_serialize);
- goto caught_signal;
- }
+ if (generation != g)
+ g = generation, i = 0;
}
if (!nodemgr_check_irm_capability(host, reset_cycles) ||
!nodemgr_do_irm_duties(host, reset_cycles)) {
reset_cycles++;
- up(&nodemgr_serialize);
+ mutex_unlock(&nodemgr_serialize);
continue;
}
reset_cycles = 0;
/* Update some of our sysfs symlinks */
nodemgr_update_host_dev_links(host);
- up(&nodemgr_serialize);
+ mutex_unlock(&nodemgr_serialize);
}
-
-caught_signal:
+unlock_exit:
+ mutex_unlock(&nodemgr_serialize);
+exit:
HPSB_VERBOSE("NodeMgr: Exiting thread");
-
- complete_and_exit(&hi->exited, 0);
+ return 0;
}
int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *))
struct host_info *hi;
hi = hpsb_create_hostinfo(&nodemgr_highlevel, host, sizeof(*hi));
-
if (!hi) {
- HPSB_ERR ("NodeMgr: out of memory in add host");
+ HPSB_ERR("NodeMgr: out of memory in add host");
return;
}
-
hi->host = host;
- init_completion(&hi->exited);
- sema_init(&hi->reset_sem, 0);
-
- sprintf(hi->daemon_name, "knodemgrd_%d", host->id);
-
- hi->pid = kernel_thread(nodemgr_host_thread, hi, CLONE_KERNEL);
-
- if (hi->pid < 0) {
- HPSB_ERR ("NodeMgr: failed to start %s thread for %s",
- hi->daemon_name, host->driver->name);
+ hi->thread = kthread_run(nodemgr_host_thread, hi, "knodemgrd_%d",
+ host->id);
+ if (IS_ERR(hi->thread)) {
+ HPSB_ERR("NodeMgr: cannot start thread for host %d", host->id);
hpsb_destroy_hostinfo(&nodemgr_highlevel, host);
- return;
}
-
- return;
}
static void nodemgr_host_reset(struct hpsb_host *host)
{
struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host);
- if (hi != NULL) {
- HPSB_VERBOSE("NodeMgr: Processing host reset for %s", hi->daemon_name);
- up(&hi->reset_sem);
- } else
- HPSB_ERR ("NodeMgr: could not process reset of unused host");
-
- return;
+ if (hi) {
+ HPSB_VERBOSE("NodeMgr: Processing reset for host %d", host->id);
+ wake_up_process(hi->thread);
+ }
}
static void nodemgr_remove_host(struct hpsb_host *host)
struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host);
if (hi) {
- if (hi->pid >= 0) {
- hi->kill_me = 1;
- mb();
- up(&hi->reset_sem);
- wait_for_completion(&hi->exited);
- nodemgr_remove_host_dev(&host->device);
- }
- } else
- HPSB_ERR("NodeMgr: host %s does not exist, cannot remove",
- host->driver->name);
-
- return;
+ kthread_stop(hi->thread);
+ nodemgr_remove_host_dev(&host->device);
+ }
}
static struct hpsb_highlevel nodemgr_highlevel = {