aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h
blob: a288fa6d72c8026f60a62625f4bf421cc6d3a74e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
/*
 * Copyright 2008 Advanced Micro Devices, Inc.
 * Copyright 2008 Red Hat Inc.
 * Copyright 2009 Jerome Glisse.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors: Dave Airlie
 *          Alex Deucher
 *          Jerome Glisse
 */
#ifndef __AMDGPU_OBJECT_H__
#define __AMDGPU_OBJECT_H__

#include <drm/amdgpu_drm.h>
#include "amdgpu.h"

#define AMDGPU_BO_INVALID_OFFSET	LONG_MAX

/* bo virtual addresses in a vm */
struct amdgpu_bo_va_mapping {
	struct list_head		list;
	struct rb_node			rb;
	uint64_t			start;
	uint64_t			last;
	uint64_t			__subtree_last;
	uint64_t			offset;
	uint64_t			flags;
};

/* User space allocated BO in a VM */
struct amdgpu_bo_va {
	struct amdgpu_vm_bo_base	base;

	/* protected by bo being reserved */
	struct dma_fence	        *last_pt_update;
	unsigned			ref_count;

	/* mappings for this bo_va */
	struct list_head		invalids;
	struct list_head		valids;
};

struct amdgpu_bo {
	/* Protected by tbo.reserved */
	u32				preferred_domains;
	u32				allowed_domains;
	struct ttm_place		placements[AMDGPU_GEM_DOMAIN_MAX + 1];
	struct ttm_placement		placement;
	struct ttm_buffer_object	tbo;
	struct ttm_bo_kmap_obj		kmap;
	u64				flags;
	unsigned			pin_count;
	u64				tiling_flags;
	u64				metadata_flags;
	void				*metadata;
	u32				metadata_size;
	unsigned			prime_shared_count;
	/* list of all virtual address to which this bo is associated to */
	struct list_head		va;
	/* Constant after initialization */
	struct drm_gem_object		gem_base;
	struct amdgpu_bo		*parent;
	struct amdgpu_bo		*shadow;

	struct ttm_bo_kmap_obj		dma_buf_vmap;
	struct amdgpu_mn		*mn;

	union {
		struct list_head	mn_list;
		struct list_head	shadow_list;
	};
};

/**
 * amdgpu_mem_type_to_domain - return domain corresponding to mem_type
 * @mem_type:	ttm memory type
 *
 * Returns corresponding domain of the ttm mem_type
 */
static inline unsigned amdgpu_mem_type_to_domain(u32 mem_type)
{
	switch (mem_type) {
	case TTM_PL_VRAM:
		return AMDGPU_GEM_DOMAIN_VRAM;
	case TTM_PL_TT:
		return AMDGPU_GEM_DOMAIN_GTT;
	case TTM_PL_SYSTEM:
		return AMDGPU_GEM_DOMAIN_CPU;
	case AMDGPU_PL_GDS:
		return AMDGPU_GEM_DOMAIN_GDS;
	case AMDGPU_PL_GWS:
		return AMDGPU_GEM_DOMAIN_GWS;
	case AMDGPU_PL_OA:
		return AMDGPU_GEM_DOMAIN_OA;
	default:
		break;
	}
	return 0;
}

/**
 * amdgpu_bo_reserve - reserve bo
 * @bo:		bo structure
 * @no_intr:	don't return -ERESTARTSYS on pending signal
 *
 * Returns:
 * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by
 * a signal. Release all buffer reservations and return to user-space.
 */
static inline int amdgpu_bo_reserve(struct amdgpu_bo *bo, bool no_intr)
{
	struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
	int r;

	r = ttm_bo_reserve(&bo->tbo, !no_intr, false, NULL);
	if (unlikely(r != 0)) {
		if (r != -ERESTARTSYS)
			dev_err(adev->dev, "%p reserve failed\n", bo);
		return r;
	}
	return 0;
}

static inline void amdgpu_bo_unreserve(struct amdgpu_bo *bo)
{
	ttm_bo_unreserve(&bo->tbo);
}

static inline unsigned long amdgpu_bo_size(struct amdgpu_bo *bo)
{
	return bo->tbo.num_pages << PAGE_SHIFT;
}

static inline unsigned amdgpu_bo_ngpu_pages(struct amdgpu_bo *bo)
{
	return (bo->tbo.num_pages << PAGE_SHIFT) / AMDGPU_GPU_PAGE_SIZE;
}

static inline unsigned amdgpu_bo_gpu_page_alignment(struct amdgpu_bo *bo)
{
	return (bo->tbo.mem.page_alignment << PAGE_SHIFT) / AMDGPU_GPU_PAGE_SIZE;
}

/**
 * amdgpu_bo_mmap_offset - return mmap offset of bo
 * @bo:	amdgpu object for which we query the offset
 *
 * Returns mmap offset of the object.
 */
static inline u64 amdgpu_bo_mmap_offset(struct amdgpu_bo *bo)
{
	return drm_vma_node_offset_addr(&bo->tbo.vma_node);
}

/**
 * amdgpu_bo_gpu_accessible - return whether the bo is currently in memory that
 * is accessible to the GPU.
 */
static inline bool amdgpu_bo_gpu_accessible(struct amdgpu_bo *bo)
{
	switch (bo->tbo.mem.mem_type) {
	case TTM_PL_TT: return amdgpu_ttm_is_bound(bo->tbo.ttm);
	case TTM_PL_VRAM: return true;
	default: return false;
	}
}

int amdgpu_bo_create(struct amdgpu_device *adev,
			    unsigned long size, int byte_align,
			    bool kernel, u32 domain, u64 flags,
			    struct sg_table *sg,
			    struct reservation_object *resv,
			    uint64_t init_value,
			    struct amdgpu_bo **bo_ptr);
int amdgpu_bo_create_restricted(struct amdgpu_device *adev,
				unsigned long size, int byte_align,
				bool kernel, u32 domain, u64 flags,
				struct sg_table *sg,
				struct ttm_placement *placement,
			        struct reservation_object *resv,
				uint64_t init_value,
				struct amdgpu_bo **bo_ptr);
int amdgpu_bo_create_reserved(struct amdgpu_device *adev,
			      unsigned long size, int align,
			      u32 domain, struct amdgpu_bo **bo_ptr,
			      u64 *gpu_addr, void **cpu_addr);
int amdgpu_bo_create_kernel(struct amdgpu_device *adev,
			    unsigned long size, int align,
			    u32 domain, struct amdgpu_bo **bo_ptr,
			    u64 *gpu_addr, void **cpu_addr);
void amdgpu_bo_free_kernel(struct amdgpu_bo **bo, u64 *gpu_addr,
			   void **cpu_addr);
int amdgpu_bo_kmap(struct amdgpu_bo *bo, void **ptr);
void *amdgpu_bo_kptr(struct amdgpu_bo *bo);
void amdgpu_bo_kunmap(struct amdgpu_bo *bo);
struct amdgpu_bo *amdgpu_bo_ref(struct amdgpu_bo *bo);
void amdgpu_bo_unref(struct amdgpu_bo **bo);
int amdgpu_bo_pin(struct amdgpu_bo *bo, u32 domain, u64 *gpu_addr);
int amdgpu_bo_pin_restricted(struct amdgpu_bo *bo, u32 domain,
			     u64 min_offset, u64 max_offset,
			     u64 *gpu_addr);
int amdgpu_bo_unpin(struct amdgpu_bo *bo);
int amdgpu_bo_evict_vram(struct amdgpu_device *adev);
int amdgpu_bo_init(struct amdgpu_device *adev);
void amdgpu_bo_fini(struct amdgpu_device *adev);
int amdgpu_bo_fbdev_mmap(struct amdgpu_bo *bo,
				struct vm_area_struct *vma);
int amdgpu_bo_set_tiling_flags(struct amdgpu_bo *bo, u64 tiling_flags);
void amdgpu_bo_get_tiling_flags(struct amdgpu_bo *bo, u64 *tiling_flags);
int amdgpu_bo_set_metadata (struct amdgpu_bo *bo, void *metadata,
			    uint32_t metadata_size, uint64_t flags);
int amdgpu_bo_get_metadata(struct amdgpu_bo *bo, void *buffer,
			   size_t buffer_size, uint32_t *metadata_size,
			   uint64_t *flags);
void amdgpu_bo_move_notify(struct ttm_buffer_object *bo,
			   bool evict,
			   struct ttm_mem_reg *new_mem);
int amdgpu_bo_fault_reserve_notify(struct ttm_buffer_object *bo);
void amdgpu_bo_fence(struct amdgpu_bo *bo, struct dma_fence *fence,
		     bool shared);
u64 amdgpu_bo_gpu_offset(struct amdgpu_bo *bo);
int amdgpu_bo_backup_to_shadow(struct amdgpu_device *adev,
			       struct amdgpu_ring *ring,
			       struct amdgpu_bo *bo,
			       struct reservation_object *resv,
			       struct dma_fence **fence, bool direct);
int amdgpu_bo_validate(struct amdgpu_bo *bo);
int amdgpu_bo_restore_from_shadow(struct amdgpu_device *adev,
				  struct amdgpu_ring *ring,
				  struct amdgpu_bo *bo,
				  struct reservation_object *resv,
				  struct dma_fence **fence,
				  bool direct);


/*
 * sub allocation
 */

static inline uint64_t amdgpu_sa_bo_gpu_addr(struct amdgpu_sa_bo *sa_bo)
{
	return sa_bo->manager->gpu_addr + sa_bo->soffset;
}

static inline void * amdgpu_sa_bo_cpu_addr(struct amdgpu_sa_bo *sa_bo)
{
	return sa_bo->manager->cpu_ptr + sa_bo->soffset;
}

int amdgpu_sa_bo_manager_init(struct amdgpu_device *adev,
				     struct amdgpu_sa_manager *sa_manager,
				     unsigned size, u32 align, u32 domain);
void amdgpu_sa_bo_manager_fini(struct amdgpu_device *adev,
				      struct amdgpu_sa_manager *sa_manager);
int amdgpu_sa_bo_manager_start(struct amdgpu_device *adev,
				      struct amdgpu_sa_manager *sa_manager);
int amdgpu_sa_bo_manager_suspend(struct amdgpu_device *adev,
					struct amdgpu_sa_manager *sa_manager);
int amdgpu_sa_bo_new(struct amdgpu_sa_manager *sa_manager,
		     struct amdgpu_sa_bo **sa_bo,
		     unsigned size, unsigned align);
void amdgpu_sa_bo_free(struct amdgpu_device *adev,
			      struct amdgpu_sa_bo **sa_bo,
			      struct dma_fence *fence);
#if defined(CONFIG_DEBUG_FS)
void amdgpu_sa_bo_dump_debug_info(struct amdgpu_sa_manager *sa_manager,
					 struct seq_file *m);
#endif


#endif
> subtract_lebs; long long available; available = c->main_bytes - c->lst.total_used; /* * Now 'available' contains theoretically available flash space * assuming there is no index, so we have to subtract the space which * is reserved for the index. */ subtract_lebs = min_idx_lebs; /* Take into account that GC reserves one LEB for its own needs */ subtract_lebs += 1; /* * The GC journal head LEB is not really accessible. And since * different write types go to different heads, we may count only on * one head's space. */ subtract_lebs += c->jhead_cnt - 1; /* We also reserve one LEB for deletions, which bypass budgeting */ subtract_lebs += 1; available -= (long long)subtract_lebs * c->leb_size; /* Subtract the dead space which is not available for use */ available -= c->lst.total_dead; /* * Subtract dark space, which might or might not be usable - it depends * on the data which we have on the media and which will be written. If * this is a lot of uncompressed or not-compressible data, the dark * space cannot be used. */ available -= c->lst.total_dark; /* * However, there is more dark space. The index may be bigger than * @min_idx_lebs. Those extra LEBs are assumed to be available, but * their dark space is not included in total_dark, so it is subtracted * here. */ if (c->lst.idx_lebs > min_idx_lebs) { subtract_lebs = c->lst.idx_lebs - min_idx_lebs; available -= subtract_lebs * c->dark_wm; } /* The calculations are rough and may end up with a negative number */ return available > 0 ? available : 0; } /** * can_use_rp - check whether the user is allowed to use reserved pool. * @c: UBIFS file-system description object * * UBIFS has so-called "reserved pool" which is flash space reserved * for the superuser and for uses whose UID/GID is recorded in UBIFS superblock. * This function checks whether current user is allowed to use reserved pool. * Returns %1 current user is allowed to use reserved pool and %0 otherwise. */ static int can_use_rp(struct ubifs_info *c) { if (current_fsuid() == c->rp_uid || capable(CAP_SYS_RESOURCE) || (c->rp_gid != 0 && in_group_p(c->rp_gid))) return 1; return 0; } /** * do_budget_space - reserve flash space for index and data growth. * @c: UBIFS file-system description object * * This function makes sure UBIFS has enough free LEBs for index growth and * data. * * When budgeting index space, UBIFS reserves thrice as many LEBs as the index * would take if it was consolidated and written to the flash. This guarantees * that the "in-the-gaps" commit method always succeeds and UBIFS will always * be able to commit dirty index. So this function basically adds amount of * budgeted index space to the size of the current index, multiplies this by 3, * and makes sure this does not exceed the amount of free LEBs. * * Notes about @c->bi.min_idx_lebs and @c->lst.idx_lebs variables: * o @c->lst.idx_lebs is the number of LEBs the index currently uses. It might * be large, because UBIFS does not do any index consolidation as long as * there is free space. IOW, the index may take a lot of LEBs, but the LEBs * will contain a lot of dirt. * o @c->bi.min_idx_lebs is the number of LEBS the index presumably takes. IOW, * the index may be consolidated to take up to @c->bi.min_idx_lebs LEBs. * * This function returns zero in case of success, and %-ENOSPC in case of * failure. */ static int do_budget_space(struct ubifs_info *c) { long long outstanding, available; int lebs, rsvd_idx_lebs, min_idx_lebs; /* First budget index space */ min_idx_lebs = ubifs_calc_min_idx_lebs(c); /* Now 'min_idx_lebs' contains number of LEBs to reserve */ if (min_idx_lebs > c->lst.idx_lebs) rsvd_idx_lebs = min_idx_lebs - c->lst.idx_lebs; else rsvd_idx_lebs = 0; /* * The number of LEBs that are available to be used by the index is: * * @c->lst.empty_lebs + @c->freeable_cnt + @c->idx_gc_cnt - * @c->lst.taken_empty_lebs * * @c->lst.empty_lebs are available because they are empty. * @c->freeable_cnt are available because they contain only free and * dirty space, @c->idx_gc_cnt are available because they are index * LEBs that have been garbage collected and are awaiting the commit * before they can be used. And the in-the-gaps method will grab these * if it needs them. @c->lst.taken_empty_lebs are empty LEBs that have * already been allocated for some purpose. * * Note, @c->idx_gc_cnt is included to both @c->lst.empty_lebs (because * these LEBs are empty) and to @c->lst.taken_empty_lebs (because they * are taken until after the commit). * * Note, @c->lst.taken_empty_lebs may temporarily be higher by one * because of the way we serialize LEB allocations and budgeting. See a * comment in 'ubifs_find_free_space()'. */ lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt - c->lst.taken_empty_lebs; if (unlikely(rsvd_idx_lebs > lebs)) { dbg_budg("out of indexing space: min_idx_lebs %d (old %d), " "rsvd_idx_lebs %d", min_idx_lebs, c->bi.min_idx_lebs, rsvd_idx_lebs); return -ENOSPC; } available = ubifs_calc_available(c, min_idx_lebs); outstanding = c->bi.data_growth + c->bi.dd_growth; if (unlikely(available < outstanding)) { dbg_budg("out of data space: available %lld, outstanding %lld", available, outstanding); return -ENOSPC; } if (available - outstanding <= c->rp_size && !can_use_rp(c)) return -ENOSPC; c->bi.min_idx_lebs = min_idx_lebs; return 0; } /** * calc_idx_growth - calculate approximate index growth from budgeting request. * @c: UBIFS file-system description object * @req: budgeting request * * For now we assume each new node adds one znode. But this is rather poor * approximation, though. */ static int calc_idx_growth(const struct ubifs_info *c, const struct ubifs_budget_req *req) { int znodes; znodes = req->new_ino + (req->new_page << UBIFS_BLOCKS_PER_PAGE_SHIFT) + req->new_dent; return znodes * c->max_idx_node_sz; } /** * calc_data_growth - calculate approximate amount of new data from budgeting * request. * @c: UBIFS file-system description object * @req: budgeting request */ static int calc_data_growth(const struct ubifs_info *c, const struct ubifs_budget_req *req) { int data_growth; data_growth = req->new_ino ? c->bi.inode_budget : 0; if (req->new_page) data_growth += c->bi.page_budget; if (req->new_dent) data_growth += c->bi.dent_budget; data_growth += req->new_ino_d; return data_growth; } /** * calc_dd_growth - calculate approximate amount of data which makes other data * dirty from budgeting request. * @c: UBIFS file-system description object * @req: budgeting request */ static int calc_dd_growth(const struct ubifs_info *c, const struct ubifs_budget_req *req) { int dd_growth; dd_growth = req->dirtied_page ? c->bi.page_budget : 0; if (req->dirtied_ino) dd_growth += c->bi.inode_budget << (req->dirtied_ino - 1); if (req->mod_dent) dd_growth += c->bi.dent_budget; dd_growth += req->dirtied_ino_d; return dd_growth; } /** * ubifs_budget_space - ensure there is enough space to complete an operation. * @c: UBIFS file-system description object * @req: budget request * * This function allocates budget for an operation. It uses pessimistic * approximation of how much flash space the operation needs. The goal of this * function is to make sure UBIFS always has flash space to flush all dirty * pages, dirty inodes, and dirty znodes (liability). This function may force * commit, garbage-collection or write-back. Returns zero in case of success, * %-ENOSPC if there is no free space and other negative error codes in case of * failures. */ int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req) { int uninitialized_var(cmt_retries), uninitialized_var(wb_retries); int err, idx_growth, data_growth, dd_growth, retried = 0; ubifs_assert(req->new_page <= 1); ubifs_assert(req->dirtied_page <= 1); ubifs_assert(req->new_dent <= 1); ubifs_assert(req->mod_dent <= 1); ubifs_assert(req->new_ino <= 1); ubifs_assert(req->new_ino_d <= UBIFS_MAX_INO_DATA); ubifs_assert(req->dirtied_ino <= 4); ubifs_assert(req->dirtied_ino_d <= UBIFS_MAX_INO_DATA * 4); ubifs_assert(!(req->new_ino_d & 7)); ubifs_assert(!(req->dirtied_ino_d & 7)); data_growth = calc_data_growth(c, req); dd_growth = calc_dd_growth(c, req); if (!data_growth && !dd_growth) return 0; idx_growth = calc_idx_growth(c, req); again: spin_lock(&c->space_lock); ubifs_assert(c->bi.idx_growth >= 0); ubifs_assert(c->bi.data_growth >= 0); ubifs_assert(c->bi.dd_growth >= 0); if (unlikely(c->bi.nospace) && (c->bi.nospace_rp || !can_use_rp(c))) { dbg_budg("no space"); spin_unlock(&c->space_lock); return -ENOSPC; } c->bi.idx_growth += idx_growth; c->bi.data_growth += data_growth; c->bi.dd_growth += dd_growth; err = do_budget_space(c); if (likely(!err)) { req->idx_growth = idx_growth; req->data_growth = data_growth; req->dd_growth = dd_growth; spin_unlock(&c->space_lock); return 0; } /* Restore the old values */ c->bi.idx_growth -= idx_growth; c->bi.data_growth -= data_growth; c->bi.dd_growth -= dd_growth; spin_unlock(&c->space_lock); if (req->fast) { dbg_budg("no space for fast budgeting"); return err; } err = make_free_space(c); cond_resched(); if (err == -EAGAIN) { dbg_budg("try again"); goto again; } else if (err == -ENOSPC) { if (!retried) { retried = 1; dbg_budg("-ENOSPC, but anyway try once again"); goto again; } dbg_budg("FS is full, -ENOSPC"); c->bi.nospace = 1; if (can_use_rp(c) || c->rp_size == 0) c->bi.nospace_rp = 1; smp_wmb(); } else ubifs_err("cannot budget space, error %d", err); return err; } /** * ubifs_release_budget - release budgeted free space. * @c: UBIFS file-system description object * @req: budget request * * This function releases the space budgeted by 'ubifs_budget_space()'. Note, * since the index changes (which were budgeted for in @req->idx_growth) will * only be written to the media on commit, this function moves the index budget * from @c->bi.idx_growth to @c->bi.uncommitted_idx. The latter will be zeroed * by the commit operation. */ void ubifs_release_budget(struct ubifs_info *c, struct ubifs_budget_req *req) { ubifs_assert(req->new_page <= 1); ubifs_assert(req->dirtied_page <= 1); ubifs_assert(req->new_dent <= 1); ubifs_assert(req->mod_dent <= 1); ubifs_assert(req->new_ino <= 1); ubifs_assert(req->new_ino_d <= UBIFS_MAX_INO_DATA); ubifs_assert(req->dirtied_ino <= 4); ubifs_assert(req->dirtied_ino_d <= UBIFS_MAX_INO_DATA * 4); ubifs_assert(!(req->new_ino_d & 7)); ubifs_assert(!(req->dirtied_ino_d & 7)); if (!req->recalculate) { ubifs_assert(req->idx_growth >= 0); ubifs_assert(req->data_growth >= 0); ubifs_assert(req->dd_growth >= 0); } if (req->recalculate) { req->data_growth = calc_data_growth(c, req); req->dd_growth = calc_dd_growth(c, req); req->idx_growth = calc_idx_growth(c, req); } if (!req->data_growth && !req->dd_growth) return; c->bi.nospace = c->bi.nospace_rp = 0; smp_wmb(); spin_lock(&c->space_lock); c->bi.idx_growth -= req->idx_growth; c->bi.uncommitted_idx += req->idx_growth; c->bi.data_growth -= req->data_growth; c->bi.dd_growth -= req->dd_growth; c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c); ubifs_assert(c->bi.idx_growth >= 0); ubifs_assert(c->bi.data_growth >= 0); ubifs_assert(c->bi.dd_growth >= 0); ubifs_assert(c->bi.min_idx_lebs < c->main_lebs); ubifs_assert(!(c->bi.idx_growth & 7)); ubifs_assert(!(c->bi.data_growth & 7)); ubifs_assert(!(c->bi.dd_growth & 7)); spin_unlock(&c->space_lock); } /** * ubifs_convert_page_budget - convert budget of a new page. * @c: UBIFS file-system description object * * This function converts budget which was allocated for a new page of data to * the budget of changing an existing page of data. The latter is smaller than * the former, so this function only does simple re-calculation and does not * involve any write-back. */ void ubifs_convert_page_budget(struct ubifs_info *c) { spin_lock(&c->space_lock); /* Release the index growth reservation */ c->bi.idx_growth -= c->max_idx_node_sz << UBIFS_BLOCKS_PER_PAGE_SHIFT; /* Release the data growth reservation */ c->bi.data_growth -= c->bi.page_budget; /* Increase the dirty data growth reservation instead */ c->bi.dd_growth += c->bi.page_budget; /* And re-calculate the indexing space reservation */ c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c); spin_unlock(&c->space_lock); } /** * ubifs_release_dirty_inode_budget - release dirty inode budget. * @c: UBIFS file-system description object * @ui: UBIFS inode to release the budget for * * This function releases budget corresponding to a dirty inode. It is usually * called when after the inode has been written to the media and marked as * clean. It also causes the "no space" flags to be cleared. */ void ubifs_release_dirty_inode_budget(struct ubifs_info *c, struct ubifs_inode *ui) { struct ubifs_budget_req req; memset(&req, 0, sizeof(struct ubifs_budget_req)); /* The "no space" flags will be cleared because dd_growth is > 0 */ req.dd_growth = c->bi.inode_budget + ALIGN(ui->data_len, 8); ubifs_release_budget(c, &req); } /** * ubifs_reported_space - calculate reported free space. * @c: the UBIFS file-system description object * @free: amount of free space * * This function calculates amount of free space which will be reported to * user-space. User-space application tend to expect that if the file-system * (e.g., via the 'statfs()' call) reports that it has N bytes available, they * are able to write a file of size N. UBIFS attaches node headers to each data * node and it has to write indexing nodes as well. This introduces additional * overhead, and UBIFS has to report slightly less free space to meet the above * expectations. * * This function assumes free space is made up of uncompressed data nodes and * full index nodes (one per data node, tripled because we always allow enough * space to write the index thrice). * * Note, the calculation is pessimistic, which means that most of the time * UBIFS reports less space than it actually has. */ long long ubifs_reported_space(const struct ubifs_info *c, long long free) { int divisor, factor, f; /* * Reported space size is @free * X, where X is UBIFS block size * divided by UBIFS block size + all overhead one data block * introduces. The overhead is the node header + indexing overhead. * * Indexing overhead calculations are based on the following formula: * I = N/(f - 1) + 1, where I - number of indexing nodes, N - number * of data nodes, f - fanout. Because effective UBIFS fanout is twice * as less than maximum fanout, we assume that each data node * introduces 3 * @c->max_idx_node_sz / (@c->fanout/2 - 1) bytes. * Note, the multiplier 3 is because UBIFS reserves thrice as more space * for the index. */ f = c->fanout > 3 ? c->fanout >> 1 : 2; factor = UBIFS_BLOCK_SIZE; divisor = UBIFS_MAX_DATA_NODE_SZ; divisor += (c->max_idx_node_sz * 3) / (f - 1); free *= factor; return div_u64(free, divisor); } /** * ubifs_get_free_space_nolock - return amount of free space. * @c: UBIFS file-system description object * * This function calculates amount of free space to report to user-space. * * Because UBIFS may introduce substantial overhead (the index, node headers, * alignment, wastage at the end of LEBs, etc), it cannot report real amount of * free flash space it has (well, because not all dirty space is reclaimable, * UBIFS does not actually know the real amount). If UBIFS did so, it would * bread user expectations about what free space is. Users seem to accustomed * to assume that if the file-system reports N bytes of free space, they would * be able to fit a file of N bytes to the FS. This almost works for * traditional file-systems, because they have way less overhead than UBIFS. * So, to keep users happy, UBIFS tries to take the overhead into account. */ long long ubifs_get_free_space_nolock(struct ubifs_info *c) { int rsvd_idx_lebs, lebs; long long available, outstanding, free; ubifs_assert(c->bi.min_idx_lebs == ubifs_calc_min_idx_lebs(c)); outstanding = c->bi.data_growth + c->bi.dd_growth; available = ubifs_calc_available(c, c->bi.min_idx_lebs); /* * When reporting free space to user-space, UBIFS guarantees that it is * possible to write a file of free space size. This means that for * empty LEBs we may use more precise calculations than * 'ubifs_calc_available()' is using. Namely, we know that in empty * LEBs we would waste only @c->leb_overhead bytes, not @c->dark_wm. * Thus, amend the available space. * * Note, the calculations below are similar to what we have in * 'do_budget_space()', so refer there for comments. */ if (c->bi.min_idx_lebs > c->lst.idx_lebs) rsvd_idx_lebs = c->bi.min_idx_lebs - c->lst.idx_lebs; else rsvd_idx_lebs = 0; lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt - c->lst.taken_empty_lebs; lebs -= rsvd_idx_lebs; available += lebs * (c->dark_wm - c->leb_overhead); if (available > outstanding) free = ubifs_reported_space(c, available - outstanding); else free = 0; return free; } /** * ubifs_get_free_space - return amount of free space. * @c: UBIFS file-system description object * * This function calculates and returns amount of free space to report to * user-space. */ long long ubifs_get_free_space(struct ubifs_info *c) { long long free; spin_lock(&c->space_lock); free = ubifs_get_free_space_nolock(c); spin_unlock(&c->space_lock); return free; }