+src/memchannel.C
[nethome.git] / src / memchannel.C
1 #include <unordered_set>
2 #include <assert.h>
3 #include <stdio.h>
4 #include <errno.h>
5 #include <sys/user.h>
6 #include <sys/mman.h>
7 #include <string.h>
8 #include <fcntl.h>
9 #include <unistd.h>
10 #include <vector>
11 #include <limits>
12 #include <thread>
13 #include <chrono>
14 #include <inttypes.h>
15 #include <set>
16
17 static std::set<volatile uint8_t *> page_set;
18
19 static size_t GB   (1024*1024*1024);
20 static size_t GBDEC(1000*1000*1000);
21
22 //#define SEQUENTIAL
23 //#define RANDOM
24
25 //#define  MAXPAGES ( 1.0*GBDEC/PAGE_SIZE)
26 #define  MAXPAGES (60.0*GBDEC/PAGE_SIZE)
27 #ifndef MAXPAGES
28 # define MAXPAGES std::numeric_limits<size_t>::max()
29 # define FREEPAGES (1.0*GBDEC/PAGE_SIZE)
30 #endif
31
32 static void stat(const char *msg) {
33   printf("%s: %zu=%zuGBDEC\n",msg,page_set.size(),page_set.size()*PAGE_SIZE/GBDEC);
34 }
35
36 static void eatmem() {
37   while (page_set.size()<MAXPAGES) {
38     void *const page(mmap(nullptr/*addr*/,PAGE_SIZE,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED|MAP_POPULATE,-1/*fd*/,0/*offset*/));
39     if (page==MAP_FAILED) {
40       if (errno==ENOMEM) {
41         stat("ENOMEM");
42         return;
43       }
44       if (errno==EAGAIN)
45         printf("ulimit -l?\n");
46       stat(strerror(errno));
47       assert(0);
48     }
49     const auto pagecast(static_cast<volatile uint8_t *>(page));
50     *pagecast=0x55;
51     const auto successpair(page_set.insert(pagecast));
52     assert(successpair.second);
53   }
54   stat("MAXPAGES");
55 }
56
57 static void freemem() {
58 #ifdef FREEPAGES
59   for (size_t pageno=0;pageno<FREEPAGES;++pageno) {
60     const auto it(page_set.begin());
61     if (it==page_set.end()) {
62       printf("freemem: Not enough memory\n");
63       assert(0);
64     }
65     const auto page(const_cast<uint8_t *>(*it));
66     int err;
67     err=munmap(page,PAGE_SIZE);
68     assert(!err);
69   }
70   stat("freemem");
71 #endif
72 }
73
74 class Dimm {
75 public:
76   const uint64_t from,to_excl;
77   Dimm(uint64_t from_,uint64_t to_excl_):from(from_),to_excl(to_excl_) {}
78   bool contains(volatile uint8_t *const page) const {
79     const uint64_t addr(reinterpret_cast<uint64_t>(page));
80     return from<=addr&&addr<to_excl;
81   }
82 };
83
84 static std::vector<Dimm> dimms;
85
86 static ssize_t dimmno_get_nocache(volatile uint8_t *const page) {
87   for (auto it=dimms.cbegin();it!=dimms.cend();++it) {
88     const auto &dimm(*it);
89     if (dimm.contains(page)) {
90 #if defined SEQUENTIAL
91       static size_t seq=0;
92       ++seq;
93       seq%=dimms.size();
94 //      return ((it-dimms.cbegin())&~3)+(seq%4);
95       return seq;
96 #elif defined RANDOM
97       return random()%dimms.size();
98 #else
99       return it-dimms.cbegin();
100 //      return dimms.size()-1-(it-dimms.cbegin());
101 //      return ((it-dimms.cbegin())&~3)+(random()%4);
102 #endif
103     }
104   }
105   //printf("Cannot find a DIMM for: %p\n",page);
106   return -1;
107 }
108
109 static ssize_t dimmno_get(volatile uint8_t *const page) {
110   static ssize_t cache_dimmno(0);
111   if (cache_dimmno==-1||!dimms[cache_dimmno].contains(page))
112     cache_dimmno=dimmno_get_nocache(page);
113   return cache_dimmno;
114 }
115
116 static size_t dimmno_to_cpuno(size_t dimmno) {
117   return 2*dimmno;
118   //return dimmno*std::thread::hardware_concurrency()/dimms.size();
119 }
120
121 static std::vector<std::unordered_set<volatile uint8_t *>> dimmno_to_pages;
122
123 static void readmap() {
124   std::vector<uint64_t> pagemapvec;
125   volatile uint8_t *const page_set_front(*page_set. cbegin());
126   volatile uint8_t *const page_set_back (*page_set.crbegin());
127   { const int fd(open("/proc/self/pagemap",O_RDONLY));
128     assert(fd!=-1);
129     pagemapvec.resize((page_set_back-page_set_front)/PAGE_SIZE+1);
130     const size_t count(pagemapvec.size()*sizeof(uint64_t));
131     const ssize_t got(pread(fd,pagemapvec.data(),count,uintptr_t(page_set_front)/PAGE_SIZE*sizeof(uint64_t)));
132     assert(size_t(got)==count);
133     int err;
134     err=close(fd);
135     assert(!err);
136   }
137   dimmno_to_pages.resize(dimms.size());
138   size_t bad_pfn_mask(0);
139   size_t bad_dimmno(0);
140   for (auto &it:page_set) {
141     volatile uint8_t *const page(it);
142     const uint64_t pagemapval(pagemapvec[(page-page_set_front)/PAGE_SIZE]);
143     //printf("%p=0x%016lx\n",page,pagemapval);
144     // vm/pagemap.txt
145     const uint64_t pfn_mask((1ULL<<55)-1); // Bits 0-54  page frame number (PFN) if present
146     if ((pagemapval&~pfn_mask)!=0x8600000000000000) {
147       ++bad_pfn_mask;
148       continue;
149     }
150     const uint64_t pfn(pagemapval&pfn_mask);
151     const auto pfn_page(reinterpret_cast<volatile uint8_t *>(pfn*PAGE_SIZE));
152     const ssize_t dimmno(dimmno_get(pfn_page));
153     if (dimmno==-1) {
154       ++bad_dimmno;
155       continue;
156     }
157     auto successpair(dimmno_to_pages[dimmno].insert(page));
158     assert(successpair.second);
159   }
160   printf("bad_pfn_mask: %zu\n",bad_pfn_mask);
161   printf("bad_dimmno:   %zu\n",bad_dimmno  );
162   for (size_t dimmno=0;dimmno<dimmno_to_pages.size();++dimmno) {
163     const Dimm &dimm(dimms[dimmno]);
164     const size_t cpuno(dimmno_to_cpuno(dimmno));
165     printf("DIMM %2zu, CPU %2zu: 0x%10" PRIx64 "-0x%10" PRIx64 "=%2g-%2gGB: %7zu pages\n",dimmno,cpuno,
166       dimm.from,dimm.to_excl,dimm.from/double(GB),dimm.to_excl/double(GB),
167       dimmno_to_pages[dimmno].size());
168   }
169 }
170
171 static void runthread(size_t dimmno) {
172   const std::unordered_set<volatile uint8_t *> &pages(dimmno_to_pages[dimmno]);
173   volatile size_t sum=0;
174   for (volatile uint8_t *const page:pages)
175     for (size_t offset=0;offset<PAGE_SIZE;offset+=64/8) // 64-bit FSB?
176       sum+=page[offset];
177 }
178
179 static void runthreads() {
180   const auto start(std::chrono::system_clock::now());
181   for (size_t loops=0;loops<10;++loops) {
182     std::vector<std::thread> threads;
183     for (size_t dimmno=0;dimmno<dimmno_to_pages.size();++dimmno) {
184       threads.push_back(std::thread(runthread,dimmno));
185       std::thread &thread(threads.back());
186       const size_t cpuno(dimmno_to_cpuno(dimmno));
187       cpu_set_t *const cpuset(CPU_ALLOC(cpuno+1));
188       assert(cpuset);
189       size_t const cpuset_size(CPU_ALLOC_SIZE(cpuno+1));
190       CPU_ZERO_S(cpuset_size,cpuset);
191       CPU_SET_S(cpuno,cpuset_size,cpuset);
192       auto native(thread.native_handle());
193       int err;
194       err=pthread_setaffinity_np(native,cpuset_size,cpuset);
195       assert(!err);
196       CPU_FREE(cpuset);
197     }
198     for (std::thread &thread:threads)
199       thread.join();
200   }
201   const auto end  (std::chrono::system_clock::now());
202   printf("%zu ms\n",std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count());
203 }
204
205 static uint64_t strtouint64(const std::string &str) {
206   static_assert(sizeof(long)==sizeof(uint64_t),"!64-bit");
207   char *end;
208   const long l(strtol(str.c_str(),&end,0/*base*/));
209   assert(l>=0);
210   assert(*end==0);
211   return l;
212 }
213
214 static void dmidecode() {
215   FILE *const f(popen("dmidecode -q -t 20","r"));
216   assert(f);
217   ssize_t dimmno(-1);
218   const auto uint64max(std::numeric_limits<uint64_t>::max());
219   uint64_t dimm_start(uint64max),dimm_end(uint64max);
220   char *linep(nullptr);
221   size_t linen(0);
222   for (;;) {
223     errno=0;
224     const ssize_t got(getline(&linep,&linen,f));
225     if (got==-1) {
226       assert_perror(errno);
227       break;
228     }
229     assert(got>=0);
230     assert(size_t(got)==strlen(linep));
231     std::string line(linep);
232     if (got==0)
233       break;
234     assert(line.back()=='\n');
235     line.erase(line.length()-1);
236     if (line=="Memory Device Mapped Address") {
237       assert(dimmno==-1||dimm_end!=uint64max);
238       ++dimmno;
239       dimm_start=dimm_end=uint64max;
240       continue;
241     }
242     { const std::string start_str("\tStarting Address: ");
243       if (line.substr(0,start_str.length())==start_str) {
244         assert(dimmno>=0);
245         assert(dimm_start==uint64max);
246         dimm_start=strtouint64(line.substr(start_str.length()));
247         assert(dimm_start!=uint64max);
248         continue;
249       }
250     }
251     { const std::string end_str("\tEnding Address: ");
252       if (line.substr(0,end_str.length())==end_str) {
253         assert(dimmno>=0);
254         assert(dimm_end==uint64max);
255         dimm_end=strtouint64(line.substr(end_str.length()));
256         assert(dimm_end!=uint64max);
257         assert(dimm_start!=uint64max);
258         if (dimm_start>=2*GB)
259           dimm_start+=2*GB;
260         if (dimm_end>=2*GB)
261           dimm_end+=2*GB;
262         dimms.push_back(Dimm(dimm_start,dimm_end));
263         continue;
264       }
265     }
266   }
267   free(linep);
268   int err;
269   err=fclose(f);
270   assert(!err);
271 }
272
273 int main() {
274   setbuf(stdout,NULL);
275   dmidecode();
276   eatmem();
277   freemem();
278   readmap();
279   runthreads();
280 }