/* * Copyright (c) 2002 by Louis Zechtzer * * Permission to use, copy and distribute this software is hereby granted * under the terms of version 2 or any later version of the GNU General Public * License, as published by the Free Software Foundation. * * THIS SOFTWARE IS PROVIDED IN ITS "AS IS" CONDITION, WITH NO WARRANTY * WHATSOEVER. NO LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING * FROM THE USE OF THIS SOFTWARE WILL BE ACCEPTED. */ /* * Authors: Louis Zechtzer (lou@clarity.net) */ #include "log.h" #include "openmosix.h" #include #include #include #include #include #include #include #include #include static int openmosix_initialized = 0; static uint16_t calculate_nodeid(const struct in_addr *); static int activate_openmosix(const struct in_addr *, int); static int openmosix_add(struct mosixnet *, int *, uint32_t, int, u_short); static int openmosix_read(int *, struct mosixnet *, int *); static int openmosix_write(int, struct mosixnet *, int); /* * openmosix_initialize: API function to activate openMosix and initialize * this module. */ int openmosix_initialize(const struct in_addr *if_addrs, int if_cnt) { if (openmosix_initialized) { return OPENMOSIX_FAIL; } if (activate_openmosix(if_addrs, if_cnt) == OPENMOSIX_FAIL) { openmosix_initialized = 0; return OPENMOSIX_FAIL; } #if !defined(ALPHA) && !defined(TESTING) log(OM_LOG_NOTICE, "Notified kernel to activate openMosix"); #else log(OM_LOG_NOTICE, "Simulated notification to activate openMosix"); #endif openmosix_initialized = 1; return OPENMOSIX_SUCCESS; } /* * openmosix_finalize: does nothing for now. * * Future: deactivate openMosix? Deactivation is much more dangerous than * activation by this daemon. If someone/thing just kills it off, at least * openMosix will continue to work, although won't be informed of new nodes. */ int openmosix_finalize() { openmosix_initialized = 0; /* deactivate_openmosix(); */ return OPENMOSIX_SUCCESS; } /* * openmosix_add_node: API call to add a node to the kernel's map. Passed * to it are, the IP address for the node to be added, an array of addresses * of aliases for that node, and a count of elements in the array of aliases. */ int openmosix_add_node(const struct in_addr *node_addr, const struct in_addr *alias, int alias_cnt) { int i, rc, node_id, fd, cnt; struct mosixnet map[MAX_MOSNET_ENTS]; if (openmosix_read(&fd, map, &cnt) == OPENMOSIX_FAIL) { return OPENMOSIX_FAIL; } node_id = calculate_nodeid(node_addr); rc = openmosix_add(map, &cnt, node_addr->s_addr, node_id, OPENMOSIX_NOT_ALIAS); if (rc == OPENMOSIX_FAIL) { close(fd); return OPENMOSIX_FAIL; } for (i = 0; i < alias_cnt; i++) { if (openmosix_add(map, &cnt, alias[i].s_addr, node_id, OPENMOSIX_ALIAS) == OPENMOSIX_FAIL) { close(fd); return OPENMOSIX_FAIL; } } if (openmosix_write(fd, map, cnt) == OPENMOSIX_FAIL) { return OPENMOSIX_FAIL; } return OPENMOSIX_SUCCESS; } /* * openmosix_read: open /proc file, read existing table into *net (which must * have a size of MAX_MOSNET_ENTS, return file descriptor suitable for writing. */ static int openmosix_read(int *fd, struct mosixnet *map, int *cnt) { int nb; #if defined(ALPHA) || defined(TESTING) *fd = open(OPENMOSIX_PROC_CONFIG, O_RDWR|O_CREAT, 0600); #else *fd = open(OPENMOSIX_PROC_CONFIG, O_RDWR); #endif if (*fd == -1) { log(OM_LOG_CRITICAL|OM_LOG_PERROR, "Unable to open %s", OPENMOSIX_PROC_CONFIG); return OPENMOSIX_FAIL; } /* XXX - Do some kind of file locking here to maintain some * compatibility with setpe. * * flock(*fd, LOCK_EX); + timer or LOCK_NB or SIGALRM */ nb = read(*fd, map, sizeof(struct mosixnet) * MAX_MOSNET_ENTS); if (nb < 0) { /* * openMosix may not be configured the first time we make * this read. Configuration happens when the map passed to * the kernel contains a node-id which matches what was * written to /proc/.../mospe. */ if (errno != ENXIO) { log(OM_LOG_ALERT|OM_LOG_PERROR, "Unable to read %s", OPENMOSIX_PROC_CONFIG); close(*fd); return OPENMOSIX_FAIL; } *cnt = 0; } else { *cnt = nb / sizeof(struct mosixnet); } return OPENMOSIX_SUCCESS; } /* * openmosix_write: write array to file descriptor, close descriptor. */ static int openmosix_write(int fd, struct mosixnet *map, int cnt) { int nb; /* Write modified map to kernel. (set file->filp = 0) */ if (lseek(fd, 0, SEEK_SET) != 0) { log(OM_LOG_ALERT|OM_LOG_PERROR, "Unable to lseek %s", OPENMOSIX_PROC_CONFIG); close(fd); return OPENMOSIX_FAIL; } #if defined(ALPHA) || defined(TESTING) ftruncate(fd, 0); #endif nb = write(fd, map, cnt * sizeof(struct mosixnet)); close(fd); if (nb != cnt * sizeof(struct mosixnet)) { log(OM_LOG_ALERT|OM_LOG_PERROR, "Unable to write %s. The" " openMosix kernel has rejected a new configuration", OPENMOSIX_PROC_CONFIG); return OPENMOSIX_FAIL; } return OPENMOSIX_SUCCESS; } /* * openmosix_add: add a node to the openMosix node map. * * Accepts the following parameters: * *map: a populated map accepting new entries * *cnt: a count of entries, which may be incremented * node_addr: IP address of node or alias to be added (network byte order) * alias: A flag to specify if node_addr is an alias * node_id: if alias, node_id is the node-id of the alias * * XXX - assure that openmosix need not be restarted. * XXX - allow multiple nodes to be added. * * XXX - this function does not allow changing of entries */ static int openmosix_add(struct mosixnet *map, int *cnt, uint32_t node_addr, int node_id, u_short alias) { int i, found, add_ahead, add_behind, node_cnt; uint32_t entry; /* * Search through map to see if node already exists, or if there is * an address collision. * * XXX - does the kernel expect the table to be sorted the same way * on each host? I don't think so. */ node_cnt = 0; add_ahead = add_behind = -1; node_addr = ntohl(node_addr); for (i = 0, found = 0; !found && i < *cnt; i++) { entry = ntohl(((struct sockaddr_in *)&map[i].saddr)-> sin_addr.s_addr); node_cnt += ((map[i].cnt == 0) ? 1 : map[i].cnt); if (node_addr >= entry && node_addr < entry + map[i].cnt) { found = 1; } else if (node_addr == entry && map[i].cnt == 0) { found = 1; } else if (node_addr == entry + map[i].cnt) { /* * If node isn't in the list, see if it can be tagged * it onto the end of an entry. */ add_ahead = i; } else if (node_addr == entry - 1) { /* * See if it can be tagged behind the beginning of an * entry. */ add_behind = i; } } /* * Add the node to the list. If add_ahead is set, then node_addr can * be tagged onto an existing entry. If add_behind is set, then * node_addr can be tagged before the beginning of an existing entry. * Otherwise, add a new entry at the end of the list. */ if (found != 1) { if (*cnt >= MOSIX_MAX) { log(OM_LOG_ALERT, "Cannot add new node. Maximum number" " of nodes: %d exceeded.", MOSIX_MAX); return OPENMOSIX_FAIL; } else if (add_ahead != -1) { map[add_ahead].cnt++; } else if (add_behind != -1) { map[add_behind].cnt++; map[add_behind].base--; ((struct sockaddr_in *)&map[add_behind].saddr)-> sin_addr.s_addr = htonl(node_addr); } else if (*cnt != MAX_MOSNET_ENTS) { if (alias == OPENMOSIX_ALIAS) { map[*cnt].cnt = 0; } else { map[*cnt].cnt = 1; } map[*cnt].base = node_id; ((struct sockaddr_in *)&map[*cnt].saddr)-> sin_family = AF_INET; ((struct sockaddr_in *)&map[*cnt].saddr)-> sin_addr.s_addr = ntohl(node_addr); ++*cnt; } else { log(OM_LOG_ALERT, "No entry space left to add new" " nodes. Maximum: %d reached", MAX_MOSNET_ENTS); return OPENMOSIX_FAIL; } } return OPENMOSIX_SUCCESS; } /* * activate_openmosix: check if openMosix is activated, and fail if it is. * Otherwise, activate openMosix. */ int activate_openmosix(const struct in_addr *if_addrs, int if_cnt) { int fd, i, n, nid; char nodeid[20]; /* 20 is from ctl_admin_mospe in /proc code */ #if defined(ALPHA) || defined (TESTING) fd = open(OPENMOSIX_PROC_MOSPE, O_WRONLY|O_CREAT|O_TRUNC, 0600); #else fd = open(OPENMOSIX_PROC_MOSPE, O_RDWR); #endif if (fd < 0) { log(OM_LOG_CRITICAL|OM_LOG_PERROR, "Unable to open %s", OPENMOSIX_PROC_MOSPE); return OPENMOSIX_FAIL; } #if !defined(ALPHA) && !defined(TESTING) n = read(fd, nodeid, 20); if (n < 0) { log(OM_LOG_CRITICAL|OM_LOG_PERROR, "Unable to read %s", OPENMOSIX_PROC_MOSPE); close(fd); return OPENMOSIX_FAIL; } else if (n < 1) { log(OM_LOG_CRITICAL, "Unable to determine current node-id" " from", OPENMOSIX_PROC_MOSPE); close(fd); return OPENMOSIX_FAIL; } if (nodeid[0] != '0') { log(OM_LOG_CRITICAL, "OpenMosix should be deactivated before" " starting this\ndaemon. (setpe -off)"); close(fd); return OPENMOSIX_FAIL; } if (lseek(fd, 0, SEEK_SET) != 0) { log(OM_LOG_CRITICAL|OM_LOG_PERROR, "Seek on %s failed.", OPENMOSIX_PROC_MOSPE); close(fd); return OPENMOSIX_FAIL; } #endif nid = calculate_nodeid(if_addrs); i = snprintf(nodeid, 20, "%d", nid); n = write(fd, nodeid, (size_t) i); close(fd); if (n < 0) { log(OM_LOG_CRITICAL|OM_LOG_PERROR, "Failed to notify" " the kernel to activate openMosix"); return OPENMOSIX_FAIL; } #if defined(ALPHA) || defined(TESTING) truncate(OPENMOSIX_PROC_CONFIG, 0); #endif /* * Add _this_ node to the map, which enables openMosix. The first * entry in if_addrs is considered the "real" address of this node, * and the rest are considered aliases. */ if (openmosix_add_node(if_addrs, if_addrs + 1, if_cnt - 1) == OPENMOSIX_FAIL) { log(OM_LOG_CRITICAL, "Failed to add local node address" " to openMosix kernel"); return OPENMOSIX_FAIL; } return OPENMOSIX_SUCCESS; } /* * calculate_nodeid: used to convert an IPv4 address to a node-id. The current * implementation is weak and will require replacement. */ static uint16_t calculate_nodeid(const struct in_addr *addr) { /* Use lower two octets of IPv4 address as a node-id */ return ntohl(addr->s_addr) & 0xffff; } #ifdef TESTING #include /* for inet_ntop() */ #include #define VERIFY(s, b, a, c) \ ((s.base == b) && \ (((struct sockaddr_in *)&s.saddr)->sin_addr.s_addr == a) && \ (s.cnt == c)) /* * To test this module, the /proc filenames are redefined in openmosix.h * to names of regular files (/tmp/something), and the testing functions * operate on them and check vaildity. */ int test_openmosix() { void test_printmap(struct mosixnet *, int); int fd, nents = 0, ob; struct mosixnet map[5]; struct in_addr a1, a2[2]; /* Write an empty file */ fd = open(OPENMOSIX_PROC_CONFIG, O_CREAT|O_TRUNC|O_WRONLY, 0600); assert(fd != -1); assert(close(fd) != -1); /* Create the following map: * * 1 10.0.0.1 3 * 8 10.0.0.8 1 */ a1.s_addr = htonl(0x0a000001); assert(openmosix_add_node(&a1, &a1, 0) == OPENMOSIX_SUCCESS); a1.s_addr = htonl(0x0a000002); assert(openmosix_add_node(&a1, &a1, 0) == OPENMOSIX_SUCCESS); a1.s_addr = htonl(0x0a000003); assert(openmosix_add_node(&a1, &a1, 0) == OPENMOSIX_SUCCESS); a1.s_addr = htonl(0x0a000008); assert(openmosix_add_node(&a1, &a1, 0) == OPENMOSIX_SUCCESS); nents = 2; fd = open(OPENMOSIX_PROC_CONFIG, O_RDONLY); assert(fd != -1); ob = read(fd, &map, nents * sizeof(struct mosixnet)); assert(ob == nents * sizeof(struct mosixnet)); close(fd); printf("Map after initial insert:\n"); test_printmap(map, nents); if (!VERIFY(map[0], 0x1, htonl(0x0a000001), 3) || !VERIFY(map[1], 0x8, htonl(0x0a000008), 1)) { printf("FAIL: Insert #1 failure\n"); return OPENMOSIX_FAIL; } /* Insert a duplicate record */ a1.s_addr = htonl(0x0a000003); assert(openmosix_add_node(&a1, &a1, 0) == OPENMOSIX_SUCCESS); fd = open(OPENMOSIX_PROC_CONFIG, O_RDONLY); assert(fd != -1); ob = read(fd, &map, nents * sizeof(struct mosixnet)); assert(ob == nents * sizeof(struct mosixnet)); close(fd); printf("Map after inserting 0x0a000003:\n"); test_printmap(map, nents); if (!VERIFY(map[0], 0x1, htonl(0x0a000001), 3) || !VERIFY(map[1], 0x8, htonl(0x0a000008), 1)) { printf("FAIL: Insert #2 failure\n"); return OPENMOSIX_FAIL; } /* Insert an independent new record */ a1.s_addr = htonl(0x0a00000a); assert(openmosix_add_node(&a1, &a1, 0) == OPENMOSIX_SUCCESS); nents++; fd = open(OPENMOSIX_PROC_CONFIG, O_RDONLY); assert(fd != -1); ob = read(fd, &map, nents * sizeof(struct mosixnet)); assert(ob == nents * sizeof(struct mosixnet)); close(fd); printf("Map after inserting 0x0a00000a:\n"); test_printmap(map, nents); if (!VERIFY(map[0], 0x1, htonl(0x0a000001), 3) || !VERIFY(map[1], 0x8, htonl(0x0a000008), 1) || !VERIFY(map[2], 0xa, htonl(0x0a00000a), 1)) { printf("FAIL: Insert #3 faulure\n"); return OPENMOSIX_FAIL; } /* Insert a new record which extends a range forwards */ a1.s_addr = htonl(0x0a000004); assert(openmosix_add_node(&a1, &a1, 0) == OPENMOSIX_SUCCESS); fd = open(OPENMOSIX_PROC_CONFIG, O_RDONLY); assert(fd != -1); ob = read(fd, &map, nents * sizeof(struct mosixnet)); assert(ob == nents * sizeof(struct mosixnet)); close(fd); printf("Map after inserting 0x0a000004:\n"); test_printmap(map, nents); if (!VERIFY(map[0], 0x1, htonl(0x0a000001), 4) || !VERIFY(map[1], 0x8, htonl(0x0a000008), 1) || !VERIFY(map[2], 0xa, htonl(0x0a00000a), 1)) { printf("FAIL: Insert #4 faulure\n"); return OPENMOSIX_FAIL; } /* Insert a new record which extends a range backwards */ a1.s_addr = htonl(0x0a000007); assert(openmosix_add_node(&a1, &a1, 0) == OPENMOSIX_SUCCESS); fd = open(OPENMOSIX_PROC_CONFIG, O_RDONLY); assert(fd != -1); ob = read(fd, &map, nents * sizeof(struct mosixnet)); assert(ob == nents * sizeof(struct mosixnet)); close(fd); printf("Map after inserting 0x0a000007:\n"); test_printmap(map, nents); if (!VERIFY(map[0], 0x1, htonl(0x0a000001), 4) || !VERIFY(map[1], 0x7, htonl(0x0a000007), 2) || !VERIFY(map[2], 0xa, htonl(0x0a00000a), 1)) { printf("FAIL: Insert #5 failure\n"); return OPENMOSIX_FAIL; } /* Add an address that is an alias */ a1.s_addr = htonl(0x0a000007); a2[0].s_addr = htonl(0x0a000010); a2[1].s_addr = htonl(0x0a000011); assert(openmosix_add_node(&a1, a2, 2) == OPENMOSIX_SUCCESS); nents += 2; fd = open(OPENMOSIX_PROC_CONFIG, O_RDONLY); assert(fd != -1); ob = read(fd, &map, nents * sizeof(struct mosixnet)); assert(ob == nents * sizeof(struct mosixnet)); close(fd); printf("Map after inserting 0x0a000007, alias 0x0a000010," " 0x0a000011:\n"); test_printmap(map, nents); if (!VERIFY(map[0], 0x1, htonl(0x0a000001), 4) || !VERIFY(map[1], 0x7, htonl(0x0a000007), 2) || !VERIFY(map[2], 0xa, htonl(0x0a00000a), 1) || !VERIFY(map[3], 0x7, htonl(0x0a000010), 0) || !VERIFY(map[4], 0x7, htonl(0x0a000011), 0)) { printf("FAIL: Insert #5 failure\n"); return OPENMOSIX_FAIL; } assert(unlink(OPENMOSIX_PROC_CONFIG) != -1); return OPENMOSIX_SUCCESS; } void test_printmap(struct mosixnet map[], int n) { int i; char dst[16]; uint32_t addr; for (i = 0; i < n; i++) { addr = ((struct sockaddr_in *)&map[i].saddr)->sin_addr.s_addr; inet_ntop(AF_INET, &addr, dst, 16); if (map[i].cnt == 0) { printf("0x%04x %s alias\n", map[i].base, dst); } else { printf("0x%04x %s %d\n", map[i].base, dst, map[i].cnt); } } } #endif