Reduce dirty records memory usage
[zfs.git] / module / zfs / zfs_quota.c
blob0fe152bc4dd8ed1d2a4e16a06fa8c3209b258e13
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or https://opensource.org/licenses/CDDL-1.0.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright (c) 2011 Pawel Jakub Dawidek
24 * Copyright (c) 2012, 2015, 2018 by Delphix. All rights reserved.
25 * Copyright (c) 2014 Integros [integros.com]
26 * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
29 /* Portions Copyright 2010 Robert Milkowski */
31 #include <sys/avl.h>
32 #include <sys/dmu_objset.h>
33 #include <sys/sa.h>
34 #include <sys/sa_impl.h>
35 #include <sys/zap.h>
36 #include <sys/zfs_project.h>
37 #include <sys/zfs_quota.h>
38 #include <sys/zfs_znode.h>
40 int
41 zpl_get_file_info(dmu_object_type_t bonustype, const void *data,
42 zfs_file_info_t *zoi)
45 * Is it a valid type of object to track?
47 if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA)
48 return (SET_ERROR(ENOENT));
50 zoi->zfi_project = ZFS_DEFAULT_PROJID;
53 * If we have a NULL data pointer
54 * then assume the id's aren't changing and
55 * return EEXIST to the dmu to let it know to
56 * use the same ids
58 if (data == NULL)
59 return (SET_ERROR(EEXIST));
61 if (bonustype == DMU_OT_ZNODE) {
62 const znode_phys_t *znp = data;
63 zoi->zfi_user = znp->zp_uid;
64 zoi->zfi_group = znp->zp_gid;
65 zoi->zfi_generation = znp->zp_gen;
66 return (0);
69 const sa_hdr_phys_t *sap = data;
70 if (sap->sa_magic == 0) {
72 * This should only happen for newly created files
73 * that haven't had the znode data filled in yet.
75 zoi->zfi_user = 0;
76 zoi->zfi_group = 0;
77 zoi->zfi_generation = 0;
78 return (0);
81 sa_hdr_phys_t sa = *sap;
82 boolean_t swap = B_FALSE;
83 if (sa.sa_magic == BSWAP_32(SA_MAGIC)) {
84 sa.sa_magic = SA_MAGIC;
85 sa.sa_layout_info = BSWAP_16(sa.sa_layout_info);
86 swap = B_TRUE;
88 VERIFY3U(sa.sa_magic, ==, SA_MAGIC);
90 int hdrsize = sa_hdrsize(&sa);
91 VERIFY3U(hdrsize, >=, sizeof (sa_hdr_phys_t));
93 uintptr_t data_after_hdr = (uintptr_t)data + hdrsize;
94 zoi->zfi_user = *((uint64_t *)(data_after_hdr + SA_UID_OFFSET));
95 zoi->zfi_group = *((uint64_t *)(data_after_hdr + SA_GID_OFFSET));
96 zoi->zfi_generation = *((uint64_t *)(data_after_hdr + SA_GEN_OFFSET));
97 uint64_t flags = *((uint64_t *)(data_after_hdr + SA_FLAGS_OFFSET));
98 if (swap)
99 flags = BSWAP_64(flags);
101 if (flags & ZFS_PROJID) {
102 zoi->zfi_project =
103 *((uint64_t *)(data_after_hdr + SA_PROJID_OFFSET));
106 if (swap) {
107 zoi->zfi_user = BSWAP_64(zoi->zfi_user);
108 zoi->zfi_group = BSWAP_64(zoi->zfi_group);
109 zoi->zfi_project = BSWAP_64(zoi->zfi_project);
110 zoi->zfi_generation = BSWAP_64(zoi->zfi_generation);
112 return (0);
115 static void
116 fuidstr_to_sid(zfsvfs_t *zfsvfs, const char *fuidstr,
117 char *domainbuf, int buflen, uid_t *ridp)
119 uint64_t fuid;
120 const char *domain;
122 fuid = zfs_strtonum(fuidstr, NULL);
124 domain = zfs_fuid_find_by_idx(zfsvfs, FUID_INDEX(fuid));
125 if (domain)
126 (void) strlcpy(domainbuf, domain, buflen);
127 else
128 domainbuf[0] = '\0';
129 *ridp = FUID_RID(fuid);
132 static uint64_t
133 zfs_userquota_prop_to_obj(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type)
135 switch (type) {
136 case ZFS_PROP_USERUSED:
137 case ZFS_PROP_USEROBJUSED:
138 return (DMU_USERUSED_OBJECT);
139 case ZFS_PROP_GROUPUSED:
140 case ZFS_PROP_GROUPOBJUSED:
141 return (DMU_GROUPUSED_OBJECT);
142 case ZFS_PROP_PROJECTUSED:
143 case ZFS_PROP_PROJECTOBJUSED:
144 return (DMU_PROJECTUSED_OBJECT);
145 case ZFS_PROP_USERQUOTA:
146 return (zfsvfs->z_userquota_obj);
147 case ZFS_PROP_GROUPQUOTA:
148 return (zfsvfs->z_groupquota_obj);
149 case ZFS_PROP_USEROBJQUOTA:
150 return (zfsvfs->z_userobjquota_obj);
151 case ZFS_PROP_GROUPOBJQUOTA:
152 return (zfsvfs->z_groupobjquota_obj);
153 case ZFS_PROP_PROJECTQUOTA:
154 return (zfsvfs->z_projectquota_obj);
155 case ZFS_PROP_PROJECTOBJQUOTA:
156 return (zfsvfs->z_projectobjquota_obj);
157 default:
158 return (ZFS_NO_OBJECT);
163 zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
164 uint64_t *cookiep, void *vbuf, uint64_t *bufsizep)
166 int error;
167 zap_cursor_t zc;
168 zap_attribute_t *za;
169 zfs_useracct_t *buf = vbuf;
170 uint64_t obj;
171 int offset = 0;
173 if (!dmu_objset_userspace_present(zfsvfs->z_os))
174 return (SET_ERROR(ENOTSUP));
176 if ((type == ZFS_PROP_PROJECTQUOTA || type == ZFS_PROP_PROJECTUSED ||
177 type == ZFS_PROP_PROJECTOBJQUOTA ||
178 type == ZFS_PROP_PROJECTOBJUSED) &&
179 !dmu_objset_projectquota_present(zfsvfs->z_os))
180 return (SET_ERROR(ENOTSUP));
182 if ((type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
183 type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA ||
184 type == ZFS_PROP_PROJECTOBJUSED ||
185 type == ZFS_PROP_PROJECTOBJQUOTA) &&
186 !dmu_objset_userobjspace_present(zfsvfs->z_os))
187 return (SET_ERROR(ENOTSUP));
189 obj = zfs_userquota_prop_to_obj(zfsvfs, type);
190 if (obj == ZFS_NO_OBJECT) {
191 *bufsizep = 0;
192 return (0);
195 if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
196 type == ZFS_PROP_PROJECTOBJUSED)
197 offset = DMU_OBJACCT_PREFIX_LEN;
199 za = zap_attribute_alloc();
200 for (zap_cursor_init_serialized(&zc, zfsvfs->z_os, obj, *cookiep);
201 (error = zap_cursor_retrieve(&zc, za)) == 0;
202 zap_cursor_advance(&zc)) {
203 if ((uintptr_t)buf - (uintptr_t)vbuf + sizeof (zfs_useracct_t) >
204 *bufsizep)
205 break;
208 * skip object quota (with zap name prefix DMU_OBJACCT_PREFIX)
209 * when dealing with block quota and vice versa.
211 if ((offset > 0) != (strncmp(za->za_name, DMU_OBJACCT_PREFIX,
212 DMU_OBJACCT_PREFIX_LEN) == 0))
213 continue;
215 fuidstr_to_sid(zfsvfs, za->za_name + offset,
216 buf->zu_domain, sizeof (buf->zu_domain), &buf->zu_rid);
218 buf->zu_space = za->za_first_integer;
219 buf++;
221 if (error == ENOENT)
222 error = 0;
224 ASSERT3U((uintptr_t)buf - (uintptr_t)vbuf, <=, *bufsizep);
225 *bufsizep = (uintptr_t)buf - (uintptr_t)vbuf;
226 *cookiep = zap_cursor_serialize(&zc);
227 zap_cursor_fini(&zc);
228 zap_attribute_free(za);
229 return (error);
233 zfs_userspace_one(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
234 const char *domain, uint64_t rid, uint64_t *valp)
236 char buf[20 + DMU_OBJACCT_PREFIX_LEN];
237 int offset = 0;
238 int err;
239 uint64_t obj;
241 *valp = 0;
243 if (!dmu_objset_userspace_present(zfsvfs->z_os))
244 return (SET_ERROR(ENOTSUP));
246 if ((type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
247 type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA ||
248 type == ZFS_PROP_PROJECTOBJUSED ||
249 type == ZFS_PROP_PROJECTOBJQUOTA) &&
250 !dmu_objset_userobjspace_present(zfsvfs->z_os))
251 return (SET_ERROR(ENOTSUP));
253 if (type == ZFS_PROP_PROJECTQUOTA || type == ZFS_PROP_PROJECTUSED ||
254 type == ZFS_PROP_PROJECTOBJQUOTA ||
255 type == ZFS_PROP_PROJECTOBJUSED) {
256 if (!dmu_objset_projectquota_present(zfsvfs->z_os))
257 return (SET_ERROR(ENOTSUP));
258 if (!zpl_is_valid_projid(rid))
259 return (SET_ERROR(EINVAL));
262 obj = zfs_userquota_prop_to_obj(zfsvfs, type);
263 if (obj == ZFS_NO_OBJECT)
264 return (0);
266 if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
267 type == ZFS_PROP_PROJECTOBJUSED) {
268 strlcpy(buf, DMU_OBJACCT_PREFIX, DMU_OBJACCT_PREFIX_LEN + 1);
269 offset = DMU_OBJACCT_PREFIX_LEN;
272 err = zfs_id_to_fuidstr(zfsvfs, domain, rid, buf + offset,
273 sizeof (buf) - offset, B_FALSE);
274 if (err)
275 return (err);
277 err = zap_lookup(zfsvfs->z_os, obj, buf, 8, 1, valp);
278 if (err == ENOENT)
279 err = 0;
280 return (err);
284 zfs_set_userquota(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
285 const char *domain, uint64_t rid, uint64_t quota)
287 char buf[32];
288 int err;
289 dmu_tx_t *tx;
290 uint64_t *objp;
291 boolean_t fuid_dirtied;
293 if (zfsvfs->z_version < ZPL_VERSION_USERSPACE)
294 return (SET_ERROR(ENOTSUP));
296 switch (type) {
297 case ZFS_PROP_USERQUOTA:
298 objp = &zfsvfs->z_userquota_obj;
299 break;
300 case ZFS_PROP_GROUPQUOTA:
301 objp = &zfsvfs->z_groupquota_obj;
302 break;
303 case ZFS_PROP_USEROBJQUOTA:
304 objp = &zfsvfs->z_userobjquota_obj;
305 break;
306 case ZFS_PROP_GROUPOBJQUOTA:
307 objp = &zfsvfs->z_groupobjquota_obj;
308 break;
309 case ZFS_PROP_PROJECTQUOTA:
310 if (!dmu_objset_projectquota_enabled(zfsvfs->z_os))
311 return (SET_ERROR(ENOTSUP));
312 if (!zpl_is_valid_projid(rid))
313 return (SET_ERROR(EINVAL));
315 objp = &zfsvfs->z_projectquota_obj;
316 break;
317 case ZFS_PROP_PROJECTOBJQUOTA:
318 if (!dmu_objset_projectquota_enabled(zfsvfs->z_os))
319 return (SET_ERROR(ENOTSUP));
320 if (!zpl_is_valid_projid(rid))
321 return (SET_ERROR(EINVAL));
323 objp = &zfsvfs->z_projectobjquota_obj;
324 break;
325 default:
326 return (SET_ERROR(EINVAL));
329 err = zfs_id_to_fuidstr(zfsvfs, domain, rid, buf, sizeof (buf), B_TRUE);
330 if (err)
331 return (err);
332 fuid_dirtied = zfsvfs->z_fuid_dirty;
334 tx = dmu_tx_create(zfsvfs->z_os);
335 dmu_tx_hold_zap(tx, *objp ? *objp : DMU_NEW_OBJECT, B_TRUE, NULL);
336 if (*objp == 0) {
337 dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, B_TRUE,
338 zfs_userquota_prop_prefixes[type]);
340 if (fuid_dirtied)
341 zfs_fuid_txhold(zfsvfs, tx);
342 err = dmu_tx_assign(tx, TXG_WAIT);
343 if (err) {
344 dmu_tx_abort(tx);
345 return (err);
348 mutex_enter(&zfsvfs->z_lock);
349 if (*objp == 0) {
350 *objp = zap_create(zfsvfs->z_os, DMU_OT_USERGROUP_QUOTA,
351 DMU_OT_NONE, 0, tx);
352 VERIFY(0 == zap_add(zfsvfs->z_os, MASTER_NODE_OBJ,
353 zfs_userquota_prop_prefixes[type], 8, 1, objp, tx));
355 mutex_exit(&zfsvfs->z_lock);
357 if (quota == 0) {
358 err = zap_remove(zfsvfs->z_os, *objp, buf, tx);
359 if (err == ENOENT)
360 err = 0;
361 } else {
362 err = zap_update(zfsvfs->z_os, *objp, buf, 8, 1, &quota, tx);
364 ASSERT(err == 0);
365 if (fuid_dirtied)
366 zfs_fuid_sync(zfsvfs, tx);
367 dmu_tx_commit(tx);
368 return (err);
371 boolean_t
372 zfs_id_overobjquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
374 char buf[20 + DMU_OBJACCT_PREFIX_LEN];
375 uint64_t used, quota, quotaobj;
376 int err;
378 if (!dmu_objset_userobjspace_present(zfsvfs->z_os)) {
379 if (dmu_objset_userobjspace_upgradable(zfsvfs->z_os)) {
380 dsl_pool_config_enter(
381 dmu_objset_pool(zfsvfs->z_os), FTAG);
382 dmu_objset_id_quota_upgrade(zfsvfs->z_os);
383 dsl_pool_config_exit(
384 dmu_objset_pool(zfsvfs->z_os), FTAG);
386 return (B_FALSE);
389 if (usedobj == DMU_PROJECTUSED_OBJECT) {
390 if (!dmu_objset_projectquota_present(zfsvfs->z_os)) {
391 if (dmu_objset_projectquota_upgradable(zfsvfs->z_os)) {
392 dsl_pool_config_enter(
393 dmu_objset_pool(zfsvfs->z_os), FTAG);
394 dmu_objset_id_quota_upgrade(zfsvfs->z_os);
395 dsl_pool_config_exit(
396 dmu_objset_pool(zfsvfs->z_os), FTAG);
398 return (B_FALSE);
400 quotaobj = zfsvfs->z_projectobjquota_obj;
401 } else if (usedobj == DMU_USERUSED_OBJECT) {
402 quotaobj = zfsvfs->z_userobjquota_obj;
403 } else if (usedobj == DMU_GROUPUSED_OBJECT) {
404 quotaobj = zfsvfs->z_groupobjquota_obj;
405 } else {
406 return (B_FALSE);
408 if (quotaobj == 0 || zfsvfs->z_replay)
409 return (B_FALSE);
411 (void) snprintf(buf, sizeof (buf), "%llx", (longlong_t)id);
412 err = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, &quota);
413 if (err != 0)
414 return (B_FALSE);
416 (void) snprintf(buf, sizeof (buf), DMU_OBJACCT_PREFIX "%llx",
417 (longlong_t)id);
418 err = zap_lookup(zfsvfs->z_os, usedobj, buf, 8, 1, &used);
419 if (err != 0)
420 return (B_FALSE);
421 return (used >= quota);
424 boolean_t
425 zfs_id_overblockquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
427 char buf[20];
428 uint64_t used, quota, quotaobj;
429 int err;
431 if (usedobj == DMU_PROJECTUSED_OBJECT) {
432 if (!dmu_objset_projectquota_present(zfsvfs->z_os)) {
433 if (dmu_objset_projectquota_upgradable(zfsvfs->z_os)) {
434 dsl_pool_config_enter(
435 dmu_objset_pool(zfsvfs->z_os), FTAG);
436 dmu_objset_id_quota_upgrade(zfsvfs->z_os);
437 dsl_pool_config_exit(
438 dmu_objset_pool(zfsvfs->z_os), FTAG);
440 return (B_FALSE);
442 quotaobj = zfsvfs->z_projectquota_obj;
443 } else if (usedobj == DMU_USERUSED_OBJECT) {
444 quotaobj = zfsvfs->z_userquota_obj;
445 } else if (usedobj == DMU_GROUPUSED_OBJECT) {
446 quotaobj = zfsvfs->z_groupquota_obj;
447 } else {
448 return (B_FALSE);
450 if (quotaobj == 0 || zfsvfs->z_replay)
451 return (B_FALSE);
453 (void) snprintf(buf, sizeof (buf), "%llx", (longlong_t)id);
454 err = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, &quota);
455 if (err != 0)
456 return (B_FALSE);
458 err = zap_lookup(zfsvfs->z_os, usedobj, buf, 8, 1, &used);
459 if (err != 0)
460 return (B_FALSE);
461 return (used >= quota);
464 boolean_t
465 zfs_id_overquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
467 return (zfs_id_overblockquota(zfsvfs, usedobj, id) ||
468 zfs_id_overobjquota(zfsvfs, usedobj, id));
471 EXPORT_SYMBOL(zpl_get_file_info);
472 EXPORT_SYMBOL(zfs_userspace_one);
473 EXPORT_SYMBOL(zfs_userspace_many);
474 EXPORT_SYMBOL(zfs_set_userquota);
475 EXPORT_SYMBOL(zfs_id_overblockquota);
476 EXPORT_SYMBOL(zfs_id_overobjquota);
477 EXPORT_SYMBOL(zfs_id_overquota);