1/*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 *    conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 *    of conditions and the following disclaimer in the documentation and/or other materials
12 *    provided with the distribution.
13 *
14 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
15 *    to endorse or promote products derived from this software without specific prior written
16 *    permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "internal.h"
32#include "proc_fs.h"
33#include "vnode.h"
34#include "path_cache.h"
35#include "los_vm_filemap.h"
36
37#ifdef LOSCFG_DEBUG_VERSION
38
39#define CLEAR_ALL_CACHE  "clear all"
40#define CLEAR_PATH_CACHE "clear pathcache"
41#define CLEAR_PAGE_CACHE "clear pagecache"
42
43static char* VnodeTypeToStr(enum VnodeType type)
44{
45    switch (type) {
46        case VNODE_TYPE_UNKNOWN:
47            return "UKN";
48        case VNODE_TYPE_REG:
49            return "REG";
50        case VNODE_TYPE_DIR:
51            return "DIR";
52        case VNODE_TYPE_BLK:
53            return "BLK";
54        case VNODE_TYPE_CHR:
55            return "CHR";
56        case VNODE_TYPE_BCHR:
57            return "BCH";
58        case VNODE_TYPE_FIFO:
59            return "FIF";
60        case VNODE_TYPE_LNK:
61            return "LNK";
62        default:
63            return "BAD";
64    }
65}
66
67static int VnodeListProcess(struct SeqBuf *buf, LIST_HEAD* list)
68{
69    int count = 0;
70    struct Vnode *item = NULL;
71    struct Vnode *nextItem = NULL;
72
73    LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(item, nextItem, list, struct Vnode, actFreeEntry) {
74        LosBufPrintf(buf, "%-10p    %-10p     %-10p    %10p    0x%08x    %-3d    %-4s    %-3d    %-3d    %-8o\t%s\n",
75            item, item->parent, item->data, item->vop, item->hash, item->useCount,
76            VnodeTypeToStr(item->type), item->gid, item->uid, item->mode, item->filePath);
77        count++;
78    }
79
80    return count;
81}
82
83static int PathCacheListProcess(struct SeqBuf *buf)
84{
85    int count = 0;
86    LIST_HEAD* bucketList = GetPathCacheList();
87
88    for (int i = 0; i < LOSCFG_MAX_PATH_CACHE_SIZE; i++) {
89        struct PathCache *pc = NULL;
90        LIST_HEAD *list = &bucketList[i];
91
92        LOS_DL_LIST_FOR_EACH_ENTRY(pc, list, struct PathCache, hashEntry) {
93            LosBufPrintf(buf, "%-3d    %-10p    %-11p    %-10p    %-9d    %s\n", i, pc,
94                pc->parentVnode, pc->childVnode, pc->hit, pc->name);
95            count++;
96        }
97    }
98
99    return count;
100}
101
102static int PageCacheEntryProcess(struct SeqBuf *buf, struct page_mapping *mapping)
103{
104    int total = 0;
105    LosFilePage *fpage = NULL;
106
107    if (mapping->nrpages == 0) {
108        LosBufPrintf(buf, "null]\n");
109        return total;
110    }
111
112    LOS_DL_LIST_FOR_EACH_ENTRY(fpage, &mapping->page_list, LosFilePage, node) {
113        LosBufPrintf(buf, "%d,", fpage->pgoff);
114        total++;
115    }
116    LosBufPrintf(buf, "]\n");
117    return total;
118}
119
120static int PageCacheMapProcess(struct SeqBuf *buf)
121{
122    LIST_HEAD *vnodeList = GetVnodeActiveList();
123    struct page_mapping *mapping = NULL;
124    struct Vnode *vnode = NULL;
125    int total = 0;
126
127    VnodeHold();
128    LOS_DL_LIST_FOR_EACH_ENTRY(vnode, vnodeList, struct Vnode, actFreeEntry) {
129        mapping = &vnode->mapping;
130        LosBufPrintf(buf, "%p, %s:[", vnode, vnode->filePath);
131        total += PageCacheEntryProcess(buf, mapping);
132    }
133    VnodeDrop();
134    return total;
135}
136
137static int FsCacheInfoFill(struct SeqBuf *buf, void *arg)
138{
139    int vnodeFree;
140    int vnodeActive;
141    int vnodeVirtual;
142    int vnodeTotal;
143
144    int pathCacheTotal;
145    int pathCacheTotalTry = 0;
146    int pathCacheTotalHit = 0;
147
148    int pageCacheTotal;
149    int pageCacheTotalTry = 0;
150    int pageCacheTotalHit = 0;
151
152    ResetPathCacheHitInfo(&pathCacheTotalHit, &pathCacheTotalTry);
153    ResetPageCacheHitInfo(&pageCacheTotalTry, &pageCacheTotalHit);
154
155    VnodeHold();
156    LosBufPrintf(buf, "\n=================================================================\n");
157    LosBufPrintf(buf,
158        "VnodeAddr     ParentAddr     DataAddr      VnodeOps      Hash           Ref    Type    Gid    Uid    Mode\n");
159    vnodeVirtual = VnodeListProcess(buf, GetVnodeVirtualList());
160    vnodeFree = VnodeListProcess(buf, GetVnodeFreeList());
161    vnodeActive = VnodeListProcess(buf, GetVnodeActiveList());
162    vnodeTotal = vnodeVirtual + vnodeFree + vnodeActive;
163
164    LosBufPrintf(buf, "\n=================================================================\n");
165    LosBufPrintf(buf, "No.    CacheAddr     ParentAddr     ChildAddr     HitCount     Name\n");
166    pathCacheTotal = PathCacheListProcess(buf);
167
168    LosBufPrintf(buf, "\n=================================================================\n");
169    pageCacheTotal = PageCacheMapProcess(buf);
170
171    LosBufPrintf(buf, "\n=================================================================\n");
172    LosBufPrintf(buf, "PathCache Total:%d Try:%d Hit:%d\n",
173        pathCacheTotal, pathCacheTotalTry, pathCacheTotalHit);
174    LosBufPrintf(buf, "Vnode Total:%d Free:%d Virtual:%d Active:%d\n",
175        vnodeTotal, vnodeFree, vnodeVirtual, vnodeActive);
176    LosBufPrintf(buf, "PageCache total:%d Try:%d Hit:%d\n", pageCacheTotal, pageCacheTotalTry, pageCacheTotalHit);
177    VnodeDrop();
178    return 0;
179}
180
181static int FsCacheClear(struct ProcFile *pf, const char *buffer, size_t buflen, loff_t *ppos)
182{
183    if (buffer == NULL || buflen < sizeof(CLEAR_ALL_CACHE)) {
184        return -EINVAL;
185    }
186    int vnodeCount = 0;
187    int pageCount = 0;
188
189    if (!strcmp(buffer, CLEAR_ALL_CACHE)) {
190        vnodeCount = VnodeClearCache();
191        pageCount = OsTryShrinkMemory(VM_FILEMAP_MAX_SCAN);
192    } else if (!strcmp(buffer, CLEAR_PAGE_CACHE)) {
193        pageCount = OsTryShrinkMemory(VM_FILEMAP_MAX_SCAN);
194    } else if (!strcmp(buffer, CLEAR_PATH_CACHE)) {
195        vnodeCount = VnodeClearCache();
196    } else {
197        return -EINVAL;
198    }
199
200    PRINTK("%d vnodes and related pathcaches cleared\n%d pages cleared\n", vnodeCount, pageCount);
201    return buflen;
202}
203static const struct ProcFileOperations FS_CACHE_PROC_FOPS = {
204    .read = FsCacheInfoFill,
205    .write = FsCacheClear,
206};
207
208void ProcFsCacheInit(void)
209{
210    struct ProcDirEntry *pde = CreateProcEntry("fs_cache", 0400, NULL);
211    if (pde == NULL) {
212        PRINT_ERR("create fs_cache error!\n");
213        return;
214    }
215
216    pde->procFileOps = &FS_CACHE_PROC_FOPS;
217}
218#else
219void ProcFsCacheInit(void)
220{
221    /* do nothing in release version */
222}
223#endif
224