/*
*Komentarze: Grzegorz Jakacki
*/
asmlinkage unsigned long sys_brk(unsigned long brk)
{
unsigned long rlim;
unsigned long newbrk, oldbrk;
/*
* Jezeli nowa wartosc ,,break'' jest zbyt mala
* (ponizej konca kodu), to sie wycofuj sie.
*/
if (brk < current->mm->end_code)
return current->mm->brk;
/*
* Zmiana wartosci ,,break'' jest istotna o tyle, o ile
* trzeba zwolnic lub przydzielic pamiec. Poniewaz zwalnia/przydziela
* sie wielokrotnosci niepodzielnych stron, nalezy obliczyc
* zaokraglenia nowej i starej wartosci ,,break'' do pelnej strony.
* Do tego sluzy makro PAGE_ALIGN (zdefiniowane w
* include/asm-386/page.h
), ktore zaokragla (w gore) adresy
* w ten sposob, ze pokazuja na pierwszy adres na stronie, np:
newbrk = PAGE_ALIGN(brk);
oldbrk = PAGE_ALIGN(current->mm->brk);
/*
* Jezeli zaokraglone wartosci sa rowne, to wystarczy tylko zmienic
* wartosc break i wrocic (= zmiana w zakresie jednej
* strony --- nie trzba nic zwalniac/przydzielac)
*/
if (oldbrk == newbrk)
return current->mm->brk = brk;
/*
* Teraz wiadomo, ze na pewno trzeba bedzie zwolnic albo przydzielic
* pamiec. Sprzwdzamy, czy nowa wartosc break jest mniejsza od
* aktualnej.
*/
if (brk <= current->mm->brk) {
/*
* TAK. Bedziemy zwalniac.
* Ustaw nowa wartosc ,,break''
*/
current->mm->brk = brk;
/*
* Zwolnij pamiec procesu o dlugosci (oldbrk-newbrk) poczawszy
* od addr (zob. mm/mmap.c:785)
*/
do_munmap(newbrk, oldbrk-newbrk);
return brk;
}
/*
* Teraz bedziemy zwiekszac break, wiec trzeba bedzie sprawdzic
* dwie rzeczy. Po pierwsze, czy nie przkroczymy limitu pamieci.
* Na rlimit
bedzie wyznaczony obowiazujacy limit
* przylaczonej pamieci operacyjnej dla biezacego procesu w bajtach.
*/
rlim = current->rlim[RLIMIT_DATA].rlim_cur;
/*
* Tu jest sztuczka. rlim jest typu unsigned long,
* a current->...rlim_cur jest typu signed long.
* RLIM_INFINITY jest najwieksza wartoscia typu long (sigend) int.
* Jezeli rlim >= RLIM_INFINITY, (tak na prawde, to moze byc
* co najwyzej rowne) to rlim jest ustawiany na ~0, czyli na
* najwieksza wartosc typu unsigned int. To jest
* najwieksza liczba, o jakiej mozna sobie pomyslec (przy
* adresowaniu na procesorze i386) wiec w praktyce oznacza
* brak ograniczenia (rozmiar przestrzeni adresowej procesora
* wyraza sie wlasnie ta liczba + 1).
* /
if (rlim >= RLIM_INFINITY)
rlim = ~0;
/*
* Jezeli rozmiar poszerzonego segmentu danych bylby zbyt duzy,
* (w sensie ograniczen nalozonych na proces) wycofuj sie.
*/
if (brk - current->mm->end_code > rlim)
return current->mm->brk;
/*
* Do szczescia pozostalo niewiele --- trzeba sprawdzic, czy
* w wirtualnej przestrzni adresowej, ktora chcielibysmy przydzielic,
* juz czegos nie ma.
*
* find_vma_intersection(proces, pocz, kon) znajduje (i zwraca) pierwszy
* (o najmniejszym adresie) segment biezacego procesu,
* ktory przecina sie z przedzialem [pocz..kon-1]
* lub NULL, jezeli takiego segmentu nie ma
* (zob. include/linux/mm.h:358)
*
* Trzeba stwierdzic, czy we fragmencie przestrzeni adresowej
* przydzielanej w wyniku zmiany wartosci break
* nie znajduje sie juz jakis segment pamieci procesu.
*
* Jezeli funkcja find_vma_intersect
* zwroci nie-NULL, to znaczy, ze cos sie znalazlo i trzeba
* sie wycofac.
*/
if (find_vma_intersection(current, oldbrk, newbrk+PAGE_SIZE))
return current->mm->brk;
/*
* Ostatnia proba: czy wystarczy pamieci. Stwierdzony powyzej fakt, ze
* mamy przestrzen adresowa (wirtualna) nie znaczy jeszcze,
* ze znajdzie sie autentyczna (fizyczna) pamiec, ktora bedzie
* mozna zamapowac w przestrzen wirtualna procesu.
*
* vm_enough_memory(p) sprawdza, czy znajdzie sie p stron pamieci.
* >> PAGE_SHIFT --- dzielenie przez rozmiar strony
* UWAGA: (newbrk-oldbrk) jest zawsze wielokrotnoscia rozmiaru strony
* (bo byly zaokraglane przez PAGE_ALIGN)
*/
if (!vm_enough_memory((newbrk-oldbrk) >> PAGE_SHIFT))
return current->mm->brk;
/*
* No to lu!
*/
current->mm->brk = brk;
/*
* Teraz wystarczy przylaczyc do procesu jakas pamiec dlugosci
* (newbrk - odlbrk) pod adres oldbrk. Pieczolowite testy
* przeprowadzane powyzej gwarantuja, ze operacja sie powiedzie.
*/
do_mmap(NULL, oldbrk, newbrk-oldbrk,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_FIXED|MAP_PRIVATE, 0);
return brk;
}