סמסטר אביב 2008 המעבדה למערכות מבוזרות
פרויקט SSD מגישים: צוקרמן דורון גרינגרס רונן מנחים: פרופ' חבר פרידמן רועי מר קוגן אלכס
מבוא: הפרויקט הינו פרויקט מחקרי לבדיקת האפשרות לשינוי אופן פעילות הזיכרון הוירטואלי במערכת ההפעלה לינוקס על מנת לנצל את יתרונות הזיכרון החיצוני מסוג.SSD בתחילת הפרויקט ניתנו לנו זוג מחשבי Asus EEE PC עליהם ביצענו את הבדיקות.
מונחים והגדרות: דיסק מכאני דיסק זיכרון חיצוני אלקטרו מכאני מסוג פלטות מסתובבות הכולל ראש מגנטי קורא/כותב. לצורך ביצוע גישה לכתובת בדיסק זה יש צורך בהזזה פיזית הן של הפלטות המסתובבות והן של הראש המגנטי קורא/כותב למקום המתאים.
SSD (Solid State Drive) כונן זיכרון חיצוני המדמה ממשק של דיסק קשיח ומשתמש בזיכרון מסוג Solid State כדי לשמור מידע יציב. כונן זה מאפשר להחליף את כונן הדיסק הקשיח עבור רוב האפליקציות. כונן ה SSD משתמש בטכנולויה של flash memory ועשוי להשתמש בזיכרון מסוג SRAM או DRAM (במקרה זה הוא נקרא.(RAM drive לכונן זה אין חלקים מכניים נעים, הוא עמיד יותר מבחינת שרידות וכן שקט יותר. מכיוון שאין עיכובים מכניים בדיסק זה הרי שזמן הגישה וההשהיות קטנים באופן משמעותי.
רקע טכני: ידוע כי הגישה לקריאה מזיכרון SSD מהירה יותר מהגישה לקריאה מזיכרון המבוסס על פלטות מכאניות. כמו כן, ידוע כי במערכות Linux כיום, כאשר המערכת זקוקה לדף אשר אינו קיים בזיכרון הראשי מבצעים context switch בעת הבאת הדף הנדרש, שכן זמן ביצוע החלפת הקשר במעבד קטן יותר מהזמן הדרוש להבאת דף מהזיכרון החיצוני מסוג HD מכאני.
שאלת המחקר: האם ניתן להשיג יתרון באמצעות שימוש בהמתנה פעילה (busy wait) ב kernel במקום לבצע החלפות הקשר בעת התרחשות של pagefault מבחינת ביצועים וזמן הסוללה?
שאלות נלוות כיצד ניתן לממש את אופן הטיפול ב?pagefault האם יהיו הבדלי מהירות בין המימושים השונים של אופן ביצוע ה?pagefault מתי משתלם לפעול בכל אחת מהשיטות? האם ניתן לנצל את זמן הקריאה מהדיסק לביצוע פעולות אחרות?
להלן המשימות שהוגדרו בתחילת הפרויקט: במהלך הפרויקט נשתול מנגנון בקוד של מערכת ההפעלה Linux,אשר משנה את אופן ביצוע ה Pagefault הנוכחי, כך שיבצע Context Switch רק במקרים מסוימים. המנגנון הנ"ל יכלול הגדרה של פרמטרים קבועים מראש שעל פיהם נקבע אם יש לבצע Context Switch או לא. בפרויקט זה נשווה בין ביצועי המחשב לפני השינוי ולאחריו הן בצריכת הסוללה והן במהירות ביצוע תוכניות השוואה.
סדר הפעולות בעת ביצוע pagefault בעת חריגת דף מתבצעת קריאת מערכת call) (system הגוררת מעבר ל.kernel ה kernel מבצע בקשת קריאה מהדיסק ולאחר מכן מבצע החלפת הקשר לתהליך המתוזמן הבא. התהליך השני רץ בינתיים. מתחשת פסיקת DMA הגורמת להחלפת הקשר חזרה ל.kernel ה kernel מעיר את התהליך המקורי.
Context Switch כידוע, המעבד מריץ מספר תהליכים כביכול בו-זמנית. מעשית, המעבד משהה פעילות של תהליך אחד ועובר לטפל בתהליך אחר. המיתוג בין התהליכים נקרא החלפת הקשר. לכל תהליך יש "הקשר ביצוע" context) (execution המכיל את כל המידע הדרוש לביצוע התהליך: מחסניות, רגיסטרים, דגלים, תכולת זיכרון, קבצים פתוחים. פעולת המיתוג שומרת את הקשר התהליך הנוכחי וטוענת למעבד את הקשר הביצוע של התהליך הבא. הפונקציה המבצעת את החלפת ההקשר נקראת schedule והיא בוחרת את התהליך הבא לביצוע במעבד. פונקציה זו קוראת לפונקציה הנקראת context_switch אשר מבצעת את החלפת ההקשר.
עלות ביצוע החלפת הקשר החלפת הקשר היא אחת הפעולות היקרות ביותר במערכת ההפעלה. ראשית, יש עלות שמירה וטעינה של כל הקשר במעבד. שנית, יש עלויות נוספות של cache misses המתרחשים בעת טעינת ההקשר החדש.
זכרונות ה flash החדשים וזכרונות ה SSD החדשים עובדים במהירות גישה הגדולה בכמה סדרי גודל מהדיסקים המכאניים. הדיסקים המכאניים פועלים בסדרי גודל של כ 10 מילי-שניות. לכן, עדיף היה לבצע החלפת הקשר בעת גישה לדיסק כזה ולתת לתהליך הנוכחי לישון בעת פנייה לדיסק. כיום, ניתן למצוא דיסקים מסוג SSD הפועלים בזמן גישה של כ 20 מיקרו-שניות.
לצורך השוואה, מצאנו מאמר המכמת את עלות ביצוע החלפת הקשר, בין מספר מיקרו-שניות ועד לכאלף מיקרו שניות= 1 מילי- שנייה לכל החלפה. נדגיש, כי קיימים הבדלים משמעותיים המשתנים עפ"י כמות המידע שאנו רוצים לכתוב/לקרוא מהדיסק. אם ההעברה תהיה גדולה בסדר גודל מזמן ביצוע החלפת הקשר- הרי שעדיף לנו לבצע את החלפת ההקשר.
להלן מוצגים גרפי התקורה של ביצוע context switch באלגוריתמים שונים:
פרמטר נוסף הוא ה.IO CACHE לינוקס משתמשת כמעט בכל זיכרון ה RAM לביצוע cache וממלאה אותו בקריאות readahead כלומר, אם ביקשנו לקרוא מספר בתים, נשתמש בעיקרון הלוקאליות ונקרא יחד עמם גם חלק מהדפים הבאים אחריהם. קיימת קריאת מערכת הכופה על ה kernel לקרוא מידע readahead מקובץ מסוים. ברגע שקובץ נקרא בקריאת readahead לתוך ה,cache הקריאות ממנו עשויות להסתיים תוך זמן קצר מאוד. גישה לדיסק ה IO מתבצעת רק אם המידע לא נמצא ב.cache
בנוסף, גישה לדיסק החיצוני IO) (storage אינה מתבצעת עבור תהליך בודד כפי שהיה עד לגירסת.kernel 2.4.18 כיום, ובעצם החל מ, kernel 2.6 ה IO scheduler מבצע גישה מתוחכמת. גישה זו יוצרת תור של בקשות IO ממספר תוכניות ומבצעת אותן יחד. בכך, נחסכים המעברים בין usermode ל kernelmode ולהיפך. נציין כי המעבר בין usermode ל kernelmode אינו יקר כמו ביצוע context switch מלא.
מה בתוכנית: בפרויקט זה נקבע מספר benchmarks תוכניות שונות להשוואה. נשווה בין מספר סוגים של מדיניות הבאת דף מהזיכרון החיצוני: 1. המצב הנוכחי אשר מבצע החלפת הקשר מידית, תוך טעינת הדף המבוקש ברקע. 2. טעינת הדף באופן מידי ללא ביצוע החלפת הקשר. 3. מספר מצבי ביניים של החלפת הקשר בתנאים מסוימים. לסיכום, נקבע באילו מצבים עדיף להשתמש בזיכרון SSD ולהמתין להבאת הדף הבא מבלי לבצע.Context Switch ונציג בצורה גרפית את הנתונים השונים אותם מדדנו תוך הסקה על השיפור האפשרי שבשימוש במחשבים מבוססי זיכרון.SSD
סביבת העבודה - התקנות והתחלת עבודה במהלך עבודה עם גירסת ה- Xandors המותאמת ל ASUS EEE שהיתה מותקנת על המחשבים גילינו כי חסרות בה תכונות בסיסיות לצורך ביצוע הפרויקט. תכונות אלו כללו אפשרות לבדיקת מצב סוללה בכל רגע נתון, והאפשרות להורדת קוד המקור של גירסה זו. לצורך הפרויקט התקנו גירסה של הפצת לינוקס UBUNTU 8.04 המותאמת במיוחד ל.ASUS EEE השתמשנו בגירסת קרנל 2.6.24 הכוללת התאמות מיוחדות למחשב ה.EEE
צריכת סוללה מעט נתונים סטטיסטיים אודות מחשב ה :EEE PC ריצה עם הגדרות BIOS עפ"י ברירת המחדל ומעבד ב 100% פעילות: 8,886 שניות. ריצה עם הגדרות כנ"ל ומעבד במנוחה רוב הזמן: 12305 שניות. ריצה כאשר כל ההגדרות ב BIOS במצב לא מאופשר : 12,963 שניות. ריצה עם הגדרות כנ"ל ומסך כבוי: 16,010 שניות.
פריקת הסוללה מצאנו כי המחשב צורך אנרגייה מהסוללה בצורה הקרובה ללינארית כאשר הסוללה טעונה בכלמעלה מ 50% מהקיבולת שלה, ואולם כאשר יורדים מתחת ל 50% מהקיבולת, התפרקות הסוללה אינה לינארית והיא נפרקת מהר יותר.
פריקת הסוללה בצורה לינארית כתלות בזמן לפי מדידה אחת בשניה נקבל את הגרף הבא:
כיצד נדע את מצב הסוללה? present: yes capacity state: ok charging state: discharging present rate: unknown remaining capacity: 10 mah present voltage: 6779 mv לצורך בדיקת מצב הסוללה ניתן לקרוא את: /proc/acpi/battery/bat0/state הפלט מוצג בצורה הבאה: מאחר והתפרקות הסוללה לינארית,ניתן ע"י חיסור המתח לפני ואחרי הרצת תוכנית ה benchmark לגלות את "כמות הסוללה שהתבזבזה" במהלך הריצה.
הרצת Benchmarks רצינו לבדוק את השפעת שינוי מנגנון חריגת הדף. מטרת הבדיקות שלנו היתה השוואה בין ביצועי המערכת לפני שינוי הקרנל ולאחריו. לצורך השוואה זו חיפשנו תוכניות בדיקה שיכללו יצירה של Pagefaults רבים. ריבוי של pagefaults בתוכנית הבדיקה עשוי לשקף את יכולת הביצועים של המחשב בכל אחת מהשיטות, שכן מהות השינוי היא באופן הטיפול ב.pagefaults
Tiobench תוכנית בדיקה מרובת חוטים להערכת ביצועי קלט/פלט. תוכנית זו נועדה למדוד ביצועי מערכות קבצים באמצעות 4 פעולות בסיסיות: קריאה סדרתית, קריאה אקראית, כתיבה סדרתית וכתיבה אקראית.
Bandwidth benchmarks Cached file read Memory copy (bcopy) Memory read Memory write Pipe TCP Latency benchmarks Context switching. Lmbench תוכנית זו מבצעת את הבדיקות הבאות: Networking: connection establishment, pipe, TCP, UDP, and RPC hot potato File system creates and deletes. Process creation. Signal handling System call overhead Memory read latency Miscellanious Processor clock rate calculation אנחנו התעניינו כמובן רק בחלק מהבדיקות הנ"ל.
הבעיה עם תוכניות ה Benchmarks הקיימות: תחילה ניסינו מספר תוכניות חישוביות קיימות כדוגמת tiobench,lmbench, tar חישוב הפאי, חישוב בעת ביצוע הפעלת אפליקציות, הרצת benchmark המבצע הפעלה של משחק מחשב ועוד. גילינו כי רוב תוכנות הבנצמארק הסטנדרטיות בודוקות או ביצועי מעבד נטו (ואז כמעט ואין חריגות דף) או ביצועי זיכרון(ואז אין פעולה חישובית משמעותית לביצוע בזמן המתנה לדף שמובא מהדיסק) ולרוב רצות כתהליך יחיד. עקב כך השינוי שעשינו אינו מתבטא בצורה טובה בבדיקות סטנדרטיות.
הטיפול בבעיית תוכניות ה ישנן מספר אפשרויות לטיפול בבעיה זו: Benchmarks הקיימות הרצה של מספר תוכניות Benchmark הצורכות זיכרון במקביל הרצת תוכנה "זוללת זיכרון", שדואגת ליצור חריגות דף רבות, במקביל להרצת תוכנית בדיקה. הרצת תוכנית בדיקה יעודית שכתבנו גילינו, כי לצורך ההשוואה אנו חייבים למדוד כמות חישובים לזמן נתון. תוכנית הבדיקה היעודית שכתבנו מבצעת במקביל תהליך אחד המבצע גישה סידרתית לזיכרון גדול (ויוצר ע"י כך הרבה חריגות דף) ותהליך שני המבצע פעולות חישוביות.
הצפי הוא שהשינוי האמור ב kernel ישפיע לטובה על התהליך צורך הזיכרון וישפיע לרעה על התהליך החישובי. בקרנל החדש, יש polling ולכן לא נעבור לתהליך החישובי בכל פעם שיתבצע.PF תוכנית זו מריצה את שני התהליכים במקביל לפרק זמן מסויים וסופרת את מס' האיטרציות שכל אחד מהתהליכים מבצע. התוכנית מקבלת כקלט את פרק הזמן להרצה ואת כמות הזיכרון לשימוש ומוציאה כפלט את מספר החישובים שכל אחד מהתהליכים ביצע.
ביצוע השינוי ב Kernel תחילה חיפשנו לבצע את השינוי בקוד של ה,pagefault כך שבכל פעם שיתרחש major pagefault נגרום לביצוע polling עד להגעת הדף. מהר מאוד גילינו כי אין קריאה ל context switch בתוך ה.pagefault הקריאה מתבצעת ב Block device driver (הנמצא בקובץ.(ll_rw_blk.c אל קוד זה ניגשת מערכת ההפעלה בכל פעם שפונים להעברת נתונים לאמצעי IO או מאמצעי IO כלשהו, שדרך העברת המידע אליו מתבצעת בבלוקים של.Data בכדי לדעת אם הקריאה לדריבר התבצעה עקב טיפול ב PF השתמשנו בדגל שהוספנו ל Thread Information Flags TIF ב.Thread descriptor סימנו דגל זה בכל תחילת טיפול ב pagefault ובדקנו אותו בכל קריאה לדריבר כך שאם הדגל מסומן ידענו לבצע את הטיפול ה"מיוחד".
ישנם מספר מצבים שיכולים לגרום ל :pagefault בקשת גישה לכתובת חוקית שלא הוקצתה (כלומר דף שעדיין לא היה בשימוש). במקרה זה המערכת תקצה דף חדש ותדאג למלא אותו באפסים (לינוקס תמיד מקצה דפים מאופסים) בקשת כתיבה לדף המסומן לקריאה בלבד. במקרה כזה המערכת תבדוק את מקור הבעיה אם מדובר בדף COW (דף המועתק בעת כתיבה Copy On (Write המערכת תיצור העתק שלו, תסמן אותו כניתן לכתיבה ותקצה אותו לתהליך. אם אכן נעשה נסיון כתיבה למקום ללא הרשאות ישלח סיגנל SIGSEGV (שגיאה) בקשת גישה לדף קיים אשר אינו נמצא כרגע בזכרון הראשי אלא בדיסק. נקרא גם major pagefault במקרה זה המערכת תטען את הדף הרלבנטי מהזכרון הוירטואלי. בקשת גישה לכתובת לא חוקית או לכתובת שאין לתהליך הרשאות אליה במקרה זה ישלח סיגנל SIGSEGV לתהליך (שגיאה)
איך עובד טיפול ב pagefault בקרנל: חריגת דף גורמת לקריאה לפונקציה do_page_fault אשר נמצא ב (x86 32bit שלנו - מערכת (במקרה arch/x86/mm/fault_32.c אחרי שפונקציה זאת מודאת שהחריגה הינה חריגת דף ולידית בכתובת זיכרון ולידית היא קוראת לפונקציה mm/memory.c:handle_mm_fault (שאינה תלויית ארכיטקטורה) handle_mm_fault מקצה את הכניסות הנדרשות בטבלת הדפים וקראת ל mm/memory.c:handle_pte_fault בהתאם למאפייני הכניסה בטבלת הדפים Entry) (PTE Page Table הפונקציה handle_pte_fault מחליטה אם צריך ליצור דף חדש (ולקרוא ל (do_no_page להביא דף קיים שאינו בזכרון כרגע (do_swap_page) או טיפול אחר (כגון העתקת דף.(COW מאחר ואותנו מעניין major pagefault בלבד נמשיך אל do_swap_page
סדר הביצוע בפונקציה do_swap_page
הפונ' do_swap_page בודקת האם הדף קיים במטמון ואם לא היא מריצה את mm/swap_state.c:read_swap_cache_async כדי להביא אותו מהדיסק ) fault (major read_swap_cache_async בודקת שוב ב cache אם הדף קיים (אחרי הבדיקה הקודמת יכול להיות שמנגנון ה caching טען את הדף). אם לא, היא מקצה מקום בזכרון לדף וקוראת אותו מהדיסק באמצעות הפונקציה mm/ page_io.c:swap_readpage swap_readpage יוצר פקודת (BIO (basic input output ואומר ל block/ll_rw_blk.c:submit_bio לבצע אותה. כאן אנו מגיעים לשכבת ה.block device driver
submit_bio מבצעת מספר בדיקות, מעדכנת בהתאם את הBIO ומריצה את,block/ll_rw_blk.c:generic_make_request בודקת האם קיימות בקשות אחרות בתור, אם כן מוסיפה את הבקשה החדשה לסוף תור הבקשות, ואחרת יוצרת תור חדש ומריצה את.block/ll_rw_blk.c: generic_make_request זאת בתורה מקבלת את התור של ההתקן לתוךq,מעדכנת את bio וקוראת לפונקציה q.block/ll_rw_blk.c: make_request אשר במקרה שלנו הוא מצביע לפונקציה >make_request_fn פונקציה זו מריצה מספר פקודות של ה low level driver (אם יש צורך) ובסופו של דבר קוראת ל.ll_rw_blk.c:get_request_wait.ll_rw_blk.c:get_request מריצה את get_request_wait אם התקבלה תשובה (שונה מNULL ) מחזירים את התשובה ויוצאים, אחרת נכנסים לqueue waiting של ההתקן הרלונטי ומבצעים.context switch אחרי שההתקן "מעיר אותנו" מריצים שוב את.get_request. אם יש תשובה מחזירים אותה ואם לא שוב נכנסים להמתנה על התור הנ"ל, וחוזר חלילה עד אשר מקבלים תשובה. get_request גורמת לביצוע הגישה להתקן.
השינויים שביצענו בקוד ה- kernel : מאחר וה- block device driver הוא ששם את התהליך הנוכחי בהמתנה, והדבר אינו מתבצע ע"י השיגרה המטפלת בחריגות הדף, הוספנו דגל מיוחד ב Thread Info (הוא מתאר החוט) אשר מסמן שהתהליך הזה נמצא כרגע בטיפול בחריגת דף. דגל זה נבדק בדריבר בכל קריאה לפונקציות הרלונטיות והטיפול הינו בהתאם (אם הדגל לא מסומן- טיפול רגיל, ואם הדגל מסומן- קריאת המידע מהדיסק ללא כניסה להמתנה). הגדרת הדגל נעשית בקובץ: include/asm x86/thread_info_32.h הוספנו את השורות הבאות: line 140: #define TIF_IN_PAGEFAULT 25 /* are we in pagefault */ line 156: #define _TIF_IN_PAGEFAULT (1<<TIF_IN_PAGEFAULT)
אלגוריתם לביצוע השינוי בתנאים מסוימים את הדלקת וכיבוי הדגל עשינו בשיגרה שמטפלת בחריגות דף בשם mm/memory.c הנמצאת בקובץ do_swap_page אנו מדליקים את הדגל לפני הקריאה לדריבר ומכבים אותו אחרי שהקריאה מסתיימת. בנוסף הגדרנו שה pagefault יתבצע בצורה של polling רק במידה ולתהליך נשאר זמן הגדול מהקבוע: EEE_PF_TIME_LEFT_CONST שכן אין טעם לבצע busy wait אם גם ככה כאשר הוא יסתיים, נאלץ לבצע החלפת הקשר בשל סיום הזמן שנותר לביצוע.
העברת התהליך להמתנה מתבצעת בפונקציית get_request_wait שבקובץ :block/ll_rw_blk.c בפונקציה זו אנו בודקים אם אנחנו נמצאים במהלך טיפול בחריגת דף ואם כן אנו מבצעים busy wait על הבקשה לקבלת המידע מהדיסק. אם איננו בחריגת דף- הטיפול מתבצע כרגיל.
מניעת הפרעות שעלולות להשפיע על הבדיקה כידוע Pagefaults נגרמים כתוצאה מפעולות שונות שרצות במעבד בזמן נתון, ולא מהתוכנית הבודדת אותה הרצנו באותו רגע. כלומר, כל התקני ה,IO וכן תהליכים אחרים הרצים במערכת, לרבות תהליכים של מערכת ההפעלה עצמה עשויים לגרום ל.Pagefaults כדי למנוע הפרעות בתוצאות הניסוי ניסינו לספור את משך ביצוע התוכניות תוך ניתוק מרבית אמצעי הקלט/פלט. למשל- כיבינו את מודול הרשת של מערכת ההפעלה, החשכנו את המסך בעת הריצה, ביטלנו את מודול הזיכרון הנייד בו השתמשנו וכו'.
הרצת הבדיקות הרצנו מספר בדיקות באמצעות התוכנה שלנו לפרקי זמן שונים ותוך שימוש בגדלי זיכרון שונים זאת באמצעות מספר סקריפטים שיצרנו בכדי להריץ את הבדיקות. את הנתונים אספנו וביצענו שכתבנו Parsing באמצעות סקריפטים נוספים
Time vs. Calculations 18000 16000 14000 12000 Calculations 10000 8000 Modified Original 6000 4000 2000 0 5 10 15 20 25 30 35 40 45 50 55 Time
Time vs Memory Tasks 400 350 300 250 Memory Tasks 200 150 Modified Original 100 50 0 5 10 15 20 25 30 35 40 45 50 55 Time
Long Time Test 800000 700000 600000 500000 Calculations 400000 300000 Modified Original 200000 100000 0 0 500 1000 1500 2000 2500 Time
Long Time Test 20000 18000 16000 14000 Memory Tasks 12000 10000 8000 Modified M Original M 6000 4000 2000 0 0 500 1000 1500 2000 2500 Time
ניתוח התוצאות והצגת גרפים: ניתן לראות עפ"י הגרפים, כי השינוי מבחינת התהליך החישובי- הבדל זניח. מבחינת פעילויות זיכרון הבדל זניח. מבחינת צריכת הסוללה הבדל זניח. ההנחה שלנו היא שחוסר ההבדל בתוצאות, הן בתהליך החישובי והן בתהליך צורך הזיכרון מעידה על כך שאכן תהליך ביצוע ה context switch הינו תהליך יקר ואפילו התהליך החישובי שאמור להפסיד מה busywait (כי אינו מקבל זמן מעבד בזמן הזה) דוקא מרוויח מאחר ויש במערכת פחות החלפות הקשר, שהן כפי שציינו, פעולות יקרות במיוחד במערכת. מנגנון התיזמון של לינוקס גורם לכך שבסופו של דבר כל תהליך יקבל את הזמן המגיע לו במהלך ה.epoch
מסקנות: עפ"י התוצאות שבידינו, אין יתרון לביצוע busy wait במקום context switch לא מבחינת ביצועים ולא מבחינת צריכת הסוללה. אף על פי כן, אנו מאמינים כי בזמן הקרוב, עם התפתחות הטכנולוגיה גישה זו אכן עשויה להביא תועלת מרובה.
רעיונות להמשך.Paging הצעות לפרויקט המשך: ביצוע segmentation מה SSD במקום כל קריאה מהדיסק (של קובץ וכו') תבצע.polling
כל שנותר הוא לומר תודה. ברצוננו להודות לכל הגורמים שתרמו להצלחת הפרויקט: פרופסור המעבדה: פרופ' רועי פרידמן. המנחה המסור: מר אלכס קוגן. ולכל צוות מעבדת ה.DSL בהזדמנות זו נודה גם לחברי Haifux מועדון הלינוקס החיפאי שתרמו ועזרו לקידום הרעיונות.