add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,189 @@
/*
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __XFS_DQUOT_H__
#define __XFS_DQUOT_H__
/*
* Dquots are structures that hold quota information about a user or a group,
* much like inodes are for files. In fact, dquots share many characteristics
* with inodes. However, dquots can also be a centralized resource, relative
* to a collection of inodes. In this respect, dquots share some characteristics
* of the superblock.
* XFS dquots exploit both those in its algorithms. They make every attempt
* to not be a bottleneck when quotas are on and have minimal impact, if any,
* when quotas are off.
*/
/*
* The hash chain headers (hash buckets)
*/
typedef struct xfs_dqhash {
struct xfs_dquot *qh_next;
struct mutex qh_lock;
uint qh_version; /* ever increasing version */
uint qh_nelems; /* number of dquots on the list */
} xfs_dqhash_t;
typedef struct xfs_dqlink {
struct xfs_dquot *ql_next; /* forward link */
struct xfs_dquot **ql_prevp; /* pointer to prev ql_next */
} xfs_dqlink_t;
struct xfs_mount;
struct xfs_trans;
/*
* This is the marker which is designed to occupy the first few
* bytes of the xfs_dquot_t structure. Even inside this, the freelist pointers
* must come first.
* This serves as the marker ("sentinel") when we have to restart list
* iterations because of locking considerations.
*/
typedef struct xfs_dqmarker {
struct xfs_dquot*dqm_flnext; /* link to freelist: must be first */
struct xfs_dquot*dqm_flprev;
xfs_dqlink_t dqm_mplist; /* link to mount's list of dquots */
xfs_dqlink_t dqm_hashlist; /* link to the hash chain */
uint dqm_flags; /* various flags (XFS_DQ_*) */
} xfs_dqmarker_t;
/*
* The incore dquot structure
*/
typedef struct xfs_dquot {
xfs_dqmarker_t q_lists; /* list ptrs, q_flags (marker) */
xfs_dqhash_t *q_hash; /* the hashchain header */
struct xfs_mount*q_mount; /* filesystem this relates to */
struct xfs_trans*q_transp; /* trans this belongs to currently */
uint q_nrefs; /* # active refs from inodes */
xfs_daddr_t q_blkno; /* blkno of dquot buffer */
int q_bufoffset; /* off of dq in buffer (# dquots) */
xfs_fileoff_t q_fileoffset; /* offset in quotas file */
struct xfs_dquot*q_gdquot; /* group dquot, hint only */
xfs_disk_dquot_t q_core; /* actual usage & quotas */
xfs_dq_logitem_t q_logitem; /* dquot log item */
xfs_qcnt_t q_res_bcount; /* total regular nblks used+reserved */
xfs_qcnt_t q_res_icount; /* total inos allocd+reserved */
xfs_qcnt_t q_res_rtbcount;/* total realtime blks used+reserved */
struct mutex q_qlock; /* quota lock */
struct completion q_flush; /* flush completion queue */
atomic_t q_pincount; /* dquot pin count */
wait_queue_head_t q_pinwait; /* dquot pinning wait queue */
#ifdef XFS_DQUOT_TRACE
struct ktrace *q_trace; /* trace header structure */
#endif
} xfs_dquot_t;
#define dq_flnext q_lists.dqm_flnext
#define dq_flprev q_lists.dqm_flprev
#define dq_mplist q_lists.dqm_mplist
#define dq_hashlist q_lists.dqm_hashlist
#define dq_flags q_lists.dqm_flags
/*
* Lock hierachy for q_qlock:
* XFS_QLOCK_NORMAL is the implicit default,
* XFS_QLOCK_NESTED is the dquot with the higher id in xfs_dqlock2
*/
enum {
XFS_QLOCK_NORMAL = 0,
XFS_QLOCK_NESTED,
};
#define XFS_DQHOLD(dqp) ((dqp)->q_nrefs++)
/*
* Manage the q_flush completion queue embedded in the dquot. This completion
* queue synchronizes processes attempting to flush the in-core dquot back to
* disk.
*/
static inline void xfs_dqflock(xfs_dquot_t *dqp)
{
wait_for_completion(&dqp->q_flush);
}
static inline int xfs_dqflock_nowait(xfs_dquot_t *dqp)
{
return try_wait_for_completion(&dqp->q_flush);
}
static inline void xfs_dqfunlock(xfs_dquot_t *dqp)
{
complete(&dqp->q_flush);
}
#define XFS_DQ_IS_LOCKED(dqp) (mutex_is_locked(&((dqp)->q_qlock)))
#define XFS_DQ_IS_ON_FREELIST(dqp) ((dqp)->dq_flnext != (dqp))
#define XFS_DQ_IS_DIRTY(dqp) ((dqp)->dq_flags & XFS_DQ_DIRTY)
#define XFS_QM_ISUDQ(dqp) ((dqp)->dq_flags & XFS_DQ_USER)
#define XFS_QM_ISPDQ(dqp) ((dqp)->dq_flags & XFS_DQ_PROJ)
#define XFS_QM_ISGDQ(dqp) ((dqp)->dq_flags & XFS_DQ_GROUP)
#define XFS_DQ_TO_QINF(dqp) ((dqp)->q_mount->m_quotainfo)
#define XFS_DQ_TO_QIP(dqp) (XFS_QM_ISUDQ(dqp) ? \
XFS_DQ_TO_QINF(dqp)->qi_uquotaip : \
XFS_DQ_TO_QINF(dqp)->qi_gquotaip)
#define XFS_IS_THIS_QUOTA_OFF(d) (! (XFS_QM_ISUDQ(d) ? \
(XFS_IS_UQUOTA_ON((d)->q_mount)) : \
(XFS_IS_OQUOTA_ON((d)->q_mount))))
#ifdef XFS_DQUOT_TRACE
/*
* Dquot Tracing stuff.
*/
#define DQUOT_TRACE_SIZE 64
#define DQUOT_KTRACE_ENTRY 1
extern void __xfs_dqtrace_entry(xfs_dquot_t *dqp, char *func,
void *, xfs_inode_t *);
#define xfs_dqtrace_entry_ino(a,b,ip) \
__xfs_dqtrace_entry((a), (b), (void*)__return_address, (ip))
#define xfs_dqtrace_entry(a,b) \
__xfs_dqtrace_entry((a), (b), (void*)__return_address, NULL)
#else
#define xfs_dqtrace_entry(a,b)
#define xfs_dqtrace_entry_ino(a,b,ip)
#endif
#ifdef QUOTADEBUG
extern void xfs_qm_dqprint(xfs_dquot_t *);
#else
#define xfs_qm_dqprint(a)
#endif
extern void xfs_qm_dqdestroy(xfs_dquot_t *);
extern int xfs_qm_dqflush(xfs_dquot_t *, uint);
extern int xfs_qm_dqpurge(xfs_dquot_t *);
extern void xfs_qm_dqunpin_wait(xfs_dquot_t *);
extern int xfs_qm_dqlock_nowait(xfs_dquot_t *);
extern void xfs_qm_dqflock_pushbuf_wait(xfs_dquot_t *dqp);
extern void xfs_qm_adjust_dqtimers(xfs_mount_t *,
xfs_disk_dquot_t *);
extern void xfs_qm_adjust_dqlimits(xfs_mount_t *,
xfs_disk_dquot_t *);
extern int xfs_qm_dqget(xfs_mount_t *, xfs_inode_t *,
xfs_dqid_t, uint, uint, xfs_dquot_t **);
extern void xfs_qm_dqput(xfs_dquot_t *);
extern void xfs_dqlock(xfs_dquot_t *);
extern void xfs_dqlock2(xfs_dquot_t *, xfs_dquot_t *);
extern void xfs_dqunlock(xfs_dquot_t *);
extern void xfs_dqunlock_nonotify(xfs_dquot_t *);
#endif /* __XFS_DQUOT_H__ */

View File

@@ -0,0 +1,662 @@
/*
* Copyright (c) 2000-2003 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_bit.h"
#include "xfs_log.h"
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_quota.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_bmap.h"
#include "xfs_btree.h"
#include "xfs_ialloc.h"
#include "xfs_rtalloc.h"
#include "xfs_error.h"
#include "xfs_itable.h"
#include "xfs_rw.h"
#include "xfs_attr.h"
#include "xfs_buf_item.h"
#include "xfs_trans_priv.h"
#include "xfs_qm.h"
/*
* returns the number of iovecs needed to log the given dquot item.
*/
/* ARGSUSED */
STATIC uint
xfs_qm_dquot_logitem_size(
xfs_dq_logitem_t *logitem)
{
/*
* we need only two iovecs, one for the format, one for the real thing
*/
return (2);
}
/*
* fills in the vector of log iovecs for the given dquot log item.
*/
STATIC void
xfs_qm_dquot_logitem_format(
xfs_dq_logitem_t *logitem,
xfs_log_iovec_t *logvec)
{
ASSERT(logitem);
ASSERT(logitem->qli_dquot);
logvec->i_addr = (xfs_caddr_t)&logitem->qli_format;
logvec->i_len = sizeof(xfs_dq_logformat_t);
XLOG_VEC_SET_TYPE(logvec, XLOG_REG_TYPE_QFORMAT);
logvec++;
logvec->i_addr = (xfs_caddr_t)&logitem->qli_dquot->q_core;
logvec->i_len = sizeof(xfs_disk_dquot_t);
XLOG_VEC_SET_TYPE(logvec, XLOG_REG_TYPE_DQUOT);
ASSERT(2 == logitem->qli_item.li_desc->lid_size);
logitem->qli_format.qlf_size = 2;
}
/*
* Increment the pin count of the given dquot.
*/
STATIC void
xfs_qm_dquot_logitem_pin(
xfs_dq_logitem_t *logitem)
{
xfs_dquot_t *dqp = logitem->qli_dquot;
ASSERT(XFS_DQ_IS_LOCKED(dqp));
atomic_inc(&dqp->q_pincount);
}
/*
* Decrement the pin count of the given dquot, and wake up
* anyone in xfs_dqwait_unpin() if the count goes to 0. The
* dquot must have been previously pinned with a call to
* xfs_qm_dquot_logitem_pin().
*/
/* ARGSUSED */
STATIC void
xfs_qm_dquot_logitem_unpin(
xfs_dq_logitem_t *logitem,
int stale)
{
xfs_dquot_t *dqp = logitem->qli_dquot;
ASSERT(atomic_read(&dqp->q_pincount) > 0);
if (atomic_dec_and_test(&dqp->q_pincount))
wake_up(&dqp->q_pinwait);
}
/* ARGSUSED */
STATIC void
xfs_qm_dquot_logitem_unpin_remove(
xfs_dq_logitem_t *logitem,
xfs_trans_t *tp)
{
xfs_qm_dquot_logitem_unpin(logitem, 0);
}
/*
* Given the logitem, this writes the corresponding dquot entry to disk
* asynchronously. This is called with the dquot entry securely locked;
* we simply get xfs_qm_dqflush() to do the work, and unlock the dquot
* at the end.
*/
STATIC void
xfs_qm_dquot_logitem_push(
xfs_dq_logitem_t *logitem)
{
xfs_dquot_t *dqp;
int error;
dqp = logitem->qli_dquot;
ASSERT(XFS_DQ_IS_LOCKED(dqp));
ASSERT(!completion_done(&dqp->q_flush));
/*
* Since we were able to lock the dquot's flush lock and
* we found it on the AIL, the dquot must be dirty. This
* is because the dquot is removed from the AIL while still
* holding the flush lock in xfs_dqflush_done(). Thus, if
* we found it in the AIL and were able to obtain the flush
* lock without sleeping, then there must not have been
* anyone in the process of flushing the dquot.
*/
error = xfs_qm_dqflush(dqp, XFS_QMOPT_DELWRI);
if (error)
xfs_fs_cmn_err(CE_WARN, dqp->q_mount,
"xfs_qm_dquot_logitem_push: push error %d on dqp %p",
error, dqp);
xfs_dqunlock(dqp);
}
/*ARGSUSED*/
STATIC xfs_lsn_t
xfs_qm_dquot_logitem_committed(
xfs_dq_logitem_t *l,
xfs_lsn_t lsn)
{
/*
* We always re-log the entire dquot when it becomes dirty,
* so, the latest copy _is_ the only one that matters.
*/
return (lsn);
}
/*
* This is called to wait for the given dquot to be unpinned.
* Most of these pin/unpin routines are plagiarized from inode code.
*/
void
xfs_qm_dqunpin_wait(
xfs_dquot_t *dqp)
{
ASSERT(XFS_DQ_IS_LOCKED(dqp));
if (atomic_read(&dqp->q_pincount) == 0)
return;
/*
* Give the log a push so we don't wait here too long.
*/
xfs_log_force(dqp->q_mount, (xfs_lsn_t)0, XFS_LOG_FORCE);
wait_event(dqp->q_pinwait, (atomic_read(&dqp->q_pincount) == 0));
}
/*
* This is called when IOP_TRYLOCK returns XFS_ITEM_PUSHBUF to indicate that
* the dquot is locked by us, but the flush lock isn't. So, here we are
* going to see if the relevant dquot buffer is incore, waiting on DELWRI.
* If so, we want to push it out to help us take this item off the AIL as soon
* as possible.
*
* We must not be holding the AIL lock at this point. Calling incore() to
* search the buffer cache can be a time consuming thing, and AIL lock is a
* spinlock.
*/
STATIC void
xfs_qm_dquot_logitem_pushbuf(
xfs_dq_logitem_t *qip)
{
xfs_dquot_t *dqp;
xfs_mount_t *mp;
xfs_buf_t *bp;
uint dopush;
dqp = qip->qli_dquot;
ASSERT(XFS_DQ_IS_LOCKED(dqp));
/*
* The qli_pushbuf_flag keeps others from
* trying to duplicate our effort.
*/
ASSERT(qip->qli_pushbuf_flag != 0);
ASSERT(qip->qli_push_owner == current_pid());
/*
* If flushlock isn't locked anymore, chances are that the
* inode flush completed and the inode was taken off the AIL.
* So, just get out.
*/
if (completion_done(&dqp->q_flush) ||
((qip->qli_item.li_flags & XFS_LI_IN_AIL) == 0)) {
qip->qli_pushbuf_flag = 0;
xfs_dqunlock(dqp);
return;
}
mp = dqp->q_mount;
bp = xfs_incore(mp->m_ddev_targp, qip->qli_format.qlf_blkno,
XFS_QI_DQCHUNKLEN(mp),
XFS_INCORE_TRYLOCK);
if (bp != NULL) {
if (XFS_BUF_ISDELAYWRITE(bp)) {
dopush = ((qip->qli_item.li_flags & XFS_LI_IN_AIL) &&
!completion_done(&dqp->q_flush));
qip->qli_pushbuf_flag = 0;
xfs_dqunlock(dqp);
if (XFS_BUF_ISPINNED(bp)) {
xfs_log_force(mp, (xfs_lsn_t)0,
XFS_LOG_FORCE);
}
if (dopush) {
int error;
#ifdef XFSRACEDEBUG
delay_for_intr();
delay(300);
#endif
error = xfs_bawrite(mp, bp);
if (error)
xfs_fs_cmn_err(CE_WARN, mp,
"xfs_qm_dquot_logitem_pushbuf: pushbuf error %d on qip %p, bp %p",
error, qip, bp);
} else {
xfs_buf_relse(bp);
}
} else {
qip->qli_pushbuf_flag = 0;
xfs_dqunlock(dqp);
xfs_buf_relse(bp);
}
return;
}
qip->qli_pushbuf_flag = 0;
xfs_dqunlock(dqp);
}
/*
* This is called to attempt to lock the dquot associated with this
* dquot log item. Don't sleep on the dquot lock or the flush lock.
* If the flush lock is already held, indicating that the dquot has
* been or is in the process of being flushed, then see if we can
* find the dquot's buffer in the buffer cache without sleeping. If
* we can and it is marked delayed write, then we want to send it out.
* We delay doing so until the push routine, though, to avoid sleeping
* in any device strategy routines.
*/
STATIC uint
xfs_qm_dquot_logitem_trylock(
xfs_dq_logitem_t *qip)
{
xfs_dquot_t *dqp;
uint retval;
dqp = qip->qli_dquot;
if (atomic_read(&dqp->q_pincount) > 0)
return (XFS_ITEM_PINNED);
if (! xfs_qm_dqlock_nowait(dqp))
return (XFS_ITEM_LOCKED);
retval = XFS_ITEM_SUCCESS;
if (!xfs_dqflock_nowait(dqp)) {
/*
* The dquot is already being flushed. It may have been
* flushed delayed write, however, and we don't want to
* get stuck waiting for that to complete. So, we want to check
* to see if we can lock the dquot's buffer without sleeping.
* If we can and it is marked for delayed write, then we
* hold it and send it out from the push routine. We don't
* want to do that now since we might sleep in the device
* strategy routine. We also don't want to grab the buffer lock
* here because we'd like not to call into the buffer cache
* while holding the AIL lock.
* Make sure to only return PUSHBUF if we set pushbuf_flag
* ourselves. If someone else is doing it then we don't
* want to go to the push routine and duplicate their efforts.
*/
if (qip->qli_pushbuf_flag == 0) {
qip->qli_pushbuf_flag = 1;
ASSERT(qip->qli_format.qlf_blkno == dqp->q_blkno);
#ifdef DEBUG
qip->qli_push_owner = current_pid();
#endif
/*
* The dquot is left locked.
*/
retval = XFS_ITEM_PUSHBUF;
} else {
retval = XFS_ITEM_FLUSHING;
xfs_dqunlock_nonotify(dqp);
}
}
ASSERT(qip->qli_item.li_flags & XFS_LI_IN_AIL);
return (retval);
}
/*
* Unlock the dquot associated with the log item.
* Clear the fields of the dquot and dquot log item that
* are specific to the current transaction. If the
* hold flags is set, do not unlock the dquot.
*/
STATIC void
xfs_qm_dquot_logitem_unlock(
xfs_dq_logitem_t *ql)
{
xfs_dquot_t *dqp;
ASSERT(ql != NULL);
dqp = ql->qli_dquot;
ASSERT(XFS_DQ_IS_LOCKED(dqp));
/*
* Clear the transaction pointer in the dquot
*/
dqp->q_transp = NULL;
/*
* dquots are never 'held' from getting unlocked at the end of
* a transaction. Their locking and unlocking is hidden inside the
* transaction layer, within trans_commit. Hence, no LI_HOLD flag
* for the logitem.
*/
xfs_dqunlock(dqp);
}
/*
* this needs to stamp an lsn into the dquot, I think.
* rpc's that look at user dquot's would then have to
* push on the dependency recorded in the dquot
*/
/* ARGSUSED */
STATIC void
xfs_qm_dquot_logitem_committing(
xfs_dq_logitem_t *l,
xfs_lsn_t lsn)
{
return;
}
/*
* This is the ops vector for dquots
*/
static struct xfs_item_ops xfs_dquot_item_ops = {
.iop_size = (uint(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_size,
.iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*))
xfs_qm_dquot_logitem_format,
.iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_pin,
.iop_unpin = (void(*)(xfs_log_item_t*, int))
xfs_qm_dquot_logitem_unpin,
.iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t*))
xfs_qm_dquot_logitem_unpin_remove,
.iop_trylock = (uint(*)(xfs_log_item_t*))
xfs_qm_dquot_logitem_trylock,
.iop_unlock = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_unlock,
.iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t))
xfs_qm_dquot_logitem_committed,
.iop_push = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_push,
.iop_pushbuf = (void(*)(xfs_log_item_t*))
xfs_qm_dquot_logitem_pushbuf,
.iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t))
xfs_qm_dquot_logitem_committing
};
/*
* Initialize the dquot log item for a newly allocated dquot.
* The dquot isn't locked at this point, but it isn't on any of the lists
* either, so we don't care.
*/
void
xfs_qm_dquot_logitem_init(
struct xfs_dquot *dqp)
{
xfs_dq_logitem_t *lp;
lp = &dqp->q_logitem;
lp->qli_item.li_type = XFS_LI_DQUOT;
lp->qli_item.li_ops = &xfs_dquot_item_ops;
lp->qli_item.li_mountp = dqp->q_mount;
lp->qli_dquot = dqp;
lp->qli_format.qlf_type = XFS_LI_DQUOT;
lp->qli_format.qlf_id = be32_to_cpu(dqp->q_core.d_id);
lp->qli_format.qlf_blkno = dqp->q_blkno;
lp->qli_format.qlf_len = 1;
/*
* This is just the offset of this dquot within its buffer
* (which is currently 1 FSB and probably won't change).
* Hence 32 bits for this offset should be just fine.
* Alternatively, we can store (bufoffset / sizeof(xfs_dqblk_t))
* here, and recompute it at recovery time.
*/
lp->qli_format.qlf_boffset = (__uint32_t)dqp->q_bufoffset;
}
/*------------------ QUOTAOFF LOG ITEMS -------------------*/
/*
* This returns the number of iovecs needed to log the given quotaoff item.
* We only need 1 iovec for an quotaoff item. It just logs the
* quotaoff_log_format structure.
*/
/*ARGSUSED*/
STATIC uint
xfs_qm_qoff_logitem_size(xfs_qoff_logitem_t *qf)
{
return (1);
}
/*
* This is called to fill in the vector of log iovecs for the
* given quotaoff log item. We use only 1 iovec, and we point that
* at the quotaoff_log_format structure embedded in the quotaoff item.
* It is at this point that we assert that all of the extent
* slots in the quotaoff item have been filled.
*/
STATIC void
xfs_qm_qoff_logitem_format(xfs_qoff_logitem_t *qf,
xfs_log_iovec_t *log_vector)
{
ASSERT(qf->qql_format.qf_type == XFS_LI_QUOTAOFF);
log_vector->i_addr = (xfs_caddr_t)&(qf->qql_format);
log_vector->i_len = sizeof(xfs_qoff_logitem_t);
XLOG_VEC_SET_TYPE(log_vector, XLOG_REG_TYPE_QUOTAOFF);
qf->qql_format.qf_size = 1;
}
/*
* Pinning has no meaning for an quotaoff item, so just return.
*/
/*ARGSUSED*/
STATIC void
xfs_qm_qoff_logitem_pin(xfs_qoff_logitem_t *qf)
{
return;
}
/*
* Since pinning has no meaning for an quotaoff item, unpinning does
* not either.
*/
/*ARGSUSED*/
STATIC void
xfs_qm_qoff_logitem_unpin(xfs_qoff_logitem_t *qf, int stale)
{
return;
}
/*ARGSUSED*/
STATIC void
xfs_qm_qoff_logitem_unpin_remove(xfs_qoff_logitem_t *qf, xfs_trans_t *tp)
{
return;
}
/*
* Quotaoff items have no locking, so just return success.
*/
/*ARGSUSED*/
STATIC uint
xfs_qm_qoff_logitem_trylock(xfs_qoff_logitem_t *qf)
{
return XFS_ITEM_LOCKED;
}
/*
* Quotaoff items have no locking or pushing, so return failure
* so that the caller doesn't bother with us.
*/
/*ARGSUSED*/
STATIC void
xfs_qm_qoff_logitem_unlock(xfs_qoff_logitem_t *qf)
{
return;
}
/*
* The quotaoff-start-item is logged only once and cannot be moved in the log,
* so simply return the lsn at which it's been logged.
*/
/*ARGSUSED*/
STATIC xfs_lsn_t
xfs_qm_qoff_logitem_committed(xfs_qoff_logitem_t *qf, xfs_lsn_t lsn)
{
return (lsn);
}
/*
* There isn't much you can do to push on an quotaoff item. It is simply
* stuck waiting for the log to be flushed to disk.
*/
/*ARGSUSED*/
STATIC void
xfs_qm_qoff_logitem_push(xfs_qoff_logitem_t *qf)
{
return;
}
/*ARGSUSED*/
STATIC xfs_lsn_t
xfs_qm_qoffend_logitem_committed(
xfs_qoff_logitem_t *qfe,
xfs_lsn_t lsn)
{
xfs_qoff_logitem_t *qfs;
struct xfs_ail *ailp;
qfs = qfe->qql_start_lip;
ailp = qfs->qql_item.li_ailp;
spin_lock(&ailp->xa_lock);
/*
* Delete the qoff-start logitem from the AIL.
* xfs_trans_ail_delete() drops the AIL lock.
*/
xfs_trans_ail_delete(ailp, (xfs_log_item_t *)qfs);
kmem_free(qfs);
kmem_free(qfe);
return (xfs_lsn_t)-1;
}
/*
* XXX rcc - don't know quite what to do with this. I think we can
* just ignore it. The only time that isn't the case is if we allow
* the client to somehow see that quotas have been turned off in which
* we can't allow that to get back until the quotaoff hits the disk.
* So how would that happen? Also, do we need different routines for
* quotaoff start and quotaoff end? I suspect the answer is yes but
* to be sure, I need to look at the recovery code and see how quota off
* recovery is handled (do we roll forward or back or do something else).
* If we roll forwards or backwards, then we need two separate routines,
* one that does nothing and one that stamps in the lsn that matters
* (truly makes the quotaoff irrevocable). If we do something else,
* then maybe we don't need two.
*/
/* ARGSUSED */
STATIC void
xfs_qm_qoff_logitem_committing(xfs_qoff_logitem_t *qip, xfs_lsn_t commit_lsn)
{
return;
}
/* ARGSUSED */
STATIC void
xfs_qm_qoffend_logitem_committing(xfs_qoff_logitem_t *qip, xfs_lsn_t commit_lsn)
{
return;
}
static struct xfs_item_ops xfs_qm_qoffend_logitem_ops = {
.iop_size = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_size,
.iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*))
xfs_qm_qoff_logitem_format,
.iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_pin,
.iop_unpin = (void(*)(xfs_log_item_t* ,int))
xfs_qm_qoff_logitem_unpin,
.iop_unpin_remove = (void(*)(xfs_log_item_t*,xfs_trans_t*))
xfs_qm_qoff_logitem_unpin_remove,
.iop_trylock = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_trylock,
.iop_unlock = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_unlock,
.iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t))
xfs_qm_qoffend_logitem_committed,
.iop_push = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_push,
.iop_pushbuf = NULL,
.iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t))
xfs_qm_qoffend_logitem_committing
};
/*
* This is the ops vector shared by all quotaoff-start log items.
*/
static struct xfs_item_ops xfs_qm_qoff_logitem_ops = {
.iop_size = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_size,
.iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*))
xfs_qm_qoff_logitem_format,
.iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_pin,
.iop_unpin = (void(*)(xfs_log_item_t*, int))
xfs_qm_qoff_logitem_unpin,
.iop_unpin_remove = (void(*)(xfs_log_item_t*,xfs_trans_t*))
xfs_qm_qoff_logitem_unpin_remove,
.iop_trylock = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_trylock,
.iop_unlock = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_unlock,
.iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t))
xfs_qm_qoff_logitem_committed,
.iop_push = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_push,
.iop_pushbuf = NULL,
.iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t))
xfs_qm_qoff_logitem_committing
};
/*
* Allocate and initialize an quotaoff item of the correct quota type(s).
*/
xfs_qoff_logitem_t *
xfs_qm_qoff_logitem_init(
struct xfs_mount *mp,
xfs_qoff_logitem_t *start,
uint flags)
{
xfs_qoff_logitem_t *qf;
qf = (xfs_qoff_logitem_t*) kmem_zalloc(sizeof(xfs_qoff_logitem_t), KM_SLEEP);
qf->qql_item.li_type = XFS_LI_QUOTAOFF;
if (start)
qf->qql_item.li_ops = &xfs_qm_qoffend_logitem_ops;
else
qf->qql_item.li_ops = &xfs_qm_qoff_logitem_ops;
qf->qql_item.li_mountp = mp;
qf->qql_format.qf_type = XFS_LI_QUOTAOFF;
qf->qql_format.qf_flags = flags;
qf->qql_start_lip = start;
return (qf);
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2000-2003 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __XFS_DQUOT_ITEM_H__
#define __XFS_DQUOT_ITEM_H__
struct xfs_dquot;
struct xfs_trans;
struct xfs_mount;
struct xfs_qoff_logitem;
typedef struct xfs_dq_logitem {
xfs_log_item_t qli_item; /* common portion */
struct xfs_dquot *qli_dquot; /* dquot ptr */
xfs_lsn_t qli_flush_lsn; /* lsn at last flush */
unsigned short qli_pushbuf_flag; /* 1 bit used in push_ail */
#ifdef DEBUG
uint64_t qli_push_owner;
#endif
xfs_dq_logformat_t qli_format; /* logged structure */
} xfs_dq_logitem_t;
typedef struct xfs_qoff_logitem {
xfs_log_item_t qql_item; /* common portion */
struct xfs_qoff_logitem *qql_start_lip; /* qoff-start logitem, if any */
xfs_qoff_logformat_t qql_format; /* logged structure */
} xfs_qoff_logitem_t;
extern void xfs_qm_dquot_logitem_init(struct xfs_dquot *);
extern xfs_qoff_logitem_t *xfs_qm_qoff_logitem_init(struct xfs_mount *,
struct xfs_qoff_logitem *, uint);
extern xfs_qoff_logitem_t *xfs_trans_get_qoff_item(struct xfs_trans *,
struct xfs_qoff_logitem *, uint);
extern void xfs_trans_log_quotaoff_item(struct xfs_trans *,
struct xfs_qoff_logitem *);
#endif /* __XFS_DQUOT_ITEM_H__ */

2701
kernel/fs/xfs/quota/xfs_qm.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,188 @@
/*
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __XFS_QM_H__
#define __XFS_QM_H__
#include "xfs_dquot_item.h"
#include "xfs_dquot.h"
#include "xfs_quota_priv.h"
#include "xfs_qm_stats.h"
struct xfs_qm;
struct xfs_inode;
extern uint ndquot;
extern struct mutex xfs_Gqm_lock;
extern struct xfs_qm *xfs_Gqm;
extern kmem_zone_t *qm_dqzone;
extern kmem_zone_t *qm_dqtrxzone;
/*
* Used in xfs_qm_sync called by xfs_sync to count the max times that it can
* iterate over the mountpt's dquot list in one call.
*/
#define XFS_QM_SYNC_MAX_RESTARTS 7
/*
* Ditto, for xfs_qm_dqreclaim_one.
*/
#define XFS_QM_RECLAIM_MAX_RESTARTS 4
/*
* Ideal ratio of free to in use dquots. Quota manager makes an attempt
* to keep this balance.
*/
#define XFS_QM_DQFREE_RATIO 2
/*
* Dquot hashtable constants/threshold values.
*/
#define XFS_QM_HASHSIZE_LOW (PAGE_SIZE / sizeof(xfs_dqhash_t))
#define XFS_QM_HASHSIZE_HIGH ((PAGE_SIZE * 4) / sizeof(xfs_dqhash_t))
/*
* This defines the unit of allocation of dquots.
* Currently, it is just one file system block, and a 4K blk contains 30
* (136 * 30 = 4080) dquots. It's probably not worth trying to make
* this more dynamic.
* XXXsup However, if this number is changed, we have to make sure that we don't
* implicitly assume that we do allocations in chunks of a single filesystem
* block in the dquot/xqm code.
*/
#define XFS_DQUOT_CLUSTER_SIZE_FSB (xfs_filblks_t)1
/*
* When doing a quotacheck, we log dquot clusters of this many FSBs at most
* in a single transaction. We don't want to ask for too huge a log reservation.
*/
#define XFS_QM_MAX_DQCLUSTER_LOGSZ 3
typedef xfs_dqhash_t xfs_dqlist_t;
/*
* The freelist head. The first two fields match the first two in the
* xfs_dquot_t structure (in xfs_dqmarker_t)
*/
typedef struct xfs_frlist {
struct xfs_dquot *qh_next;
struct xfs_dquot *qh_prev;
struct mutex qh_lock;
uint qh_version;
uint qh_nelems;
} xfs_frlist_t;
/*
* Quota Manager (global) structure. Lives only in core.
*/
typedef struct xfs_qm {
xfs_dqlist_t *qm_usr_dqhtable;/* udquot hash table */
xfs_dqlist_t *qm_grp_dqhtable;/* gdquot hash table */
uint qm_dqhashmask; /* # buckets in dq hashtab - 1 */
xfs_frlist_t qm_dqfreelist; /* freelist of dquots */
atomic_t qm_totaldquots; /* total incore dquots */
uint qm_nrefs; /* file systems with quota on */
int qm_dqfree_ratio;/* ratio of free to inuse dquots */
kmem_zone_t *qm_dqzone; /* dquot mem-alloc zone */
kmem_zone_t *qm_dqtrxzone; /* t_dqinfo of transactions */
} xfs_qm_t;
/*
* Various quota information for individual filesystems.
* The mount structure keeps a pointer to this.
*/
typedef struct xfs_quotainfo {
xfs_inode_t *qi_uquotaip; /* user quota inode */
xfs_inode_t *qi_gquotaip; /* group quota inode */
xfs_dqlist_t qi_dqlist; /* all dquots in filesys */
int qi_dqreclaims; /* a change here indicates
a removal in the dqlist */
time_t qi_btimelimit; /* limit for blks timer */
time_t qi_itimelimit; /* limit for inodes timer */
time_t qi_rtbtimelimit;/* limit for rt blks timer */
xfs_qwarncnt_t qi_bwarnlimit; /* limit for blks warnings */
xfs_qwarncnt_t qi_iwarnlimit; /* limit for inodes warnings */
xfs_qwarncnt_t qi_rtbwarnlimit;/* limit for rt blks warnings */
struct mutex qi_quotaofflock;/* to serialize quotaoff */
xfs_filblks_t qi_dqchunklen; /* # BBs in a chunk of dqs */
uint qi_dqperchunk; /* # ondisk dqs in above chunk */
xfs_qcnt_t qi_bhardlimit; /* default data blk hard limit */
xfs_qcnt_t qi_bsoftlimit; /* default data blk soft limit */
xfs_qcnt_t qi_ihardlimit; /* default inode count hard limit */
xfs_qcnt_t qi_isoftlimit; /* default inode count soft limit */
xfs_qcnt_t qi_rtbhardlimit;/* default realtime blk hard limit */
xfs_qcnt_t qi_rtbsoftlimit;/* default realtime blk soft limit */
} xfs_quotainfo_t;
extern void xfs_trans_mod_dquot(xfs_trans_t *, xfs_dquot_t *, uint, long);
extern int xfs_trans_reserve_quota_bydquots(xfs_trans_t *, xfs_mount_t *,
xfs_dquot_t *, xfs_dquot_t *, long, long, uint);
extern void xfs_trans_dqjoin(xfs_trans_t *, xfs_dquot_t *);
extern void xfs_trans_log_dquot(xfs_trans_t *, xfs_dquot_t *);
/*
* We keep the usr and grp dquots separately so that locking will be easier
* to do at commit time. All transactions that we know of at this point
* affect no more than two dquots of one type. Hence, the TRANS_MAXDQS value.
*/
#define XFS_QM_TRANS_MAXDQS 2
typedef struct xfs_dquot_acct {
xfs_dqtrx_t dqa_usrdquots[XFS_QM_TRANS_MAXDQS];
xfs_dqtrx_t dqa_grpdquots[XFS_QM_TRANS_MAXDQS];
} xfs_dquot_acct_t;
/*
* Users are allowed to have a usage exceeding their softlimit for
* a period this long.
*/
#define XFS_QM_BTIMELIMIT (7 * 24*60*60) /* 1 week */
#define XFS_QM_RTBTIMELIMIT (7 * 24*60*60) /* 1 week */
#define XFS_QM_ITIMELIMIT (7 * 24*60*60) /* 1 week */
#define XFS_QM_BWARNLIMIT 5
#define XFS_QM_IWARNLIMIT 5
#define XFS_QM_RTBWARNLIMIT 5
extern void xfs_qm_destroy_quotainfo(xfs_mount_t *);
extern int xfs_qm_quotacheck(xfs_mount_t *);
extern int xfs_qm_write_sb_changes(xfs_mount_t *, __int64_t);
/* dquot stuff */
extern boolean_t xfs_qm_dqalloc_incore(xfs_dquot_t **);
extern int xfs_qm_dqpurge_all(xfs_mount_t *, uint);
extern void xfs_qm_dqrele_all_inodes(xfs_mount_t *, uint);
/* quota ops */
extern int xfs_qm_scall_trunc_qfiles(xfs_mount_t *, uint);
extern int xfs_qm_scall_getquota(xfs_mount_t *, xfs_dqid_t, uint,
fs_disk_quota_t *);
extern int xfs_qm_scall_setqlim(xfs_mount_t *, xfs_dqid_t, uint,
fs_disk_quota_t *);
extern int xfs_qm_scall_getqstat(xfs_mount_t *, fs_quota_stat_t *);
extern int xfs_qm_scall_quotaon(xfs_mount_t *, uint);
extern int xfs_qm_scall_quotaoff(xfs_mount_t *, uint);
/* list stuff */
extern void xfs_qm_freelist_append(xfs_frlist_t *, xfs_dquot_t *);
extern void xfs_qm_freelist_unlink(xfs_dquot_t *);
#ifdef DEBUG
extern int xfs_qm_internalqcheck(xfs_mount_t *);
#else
#define xfs_qm_internalqcheck(mp) (0)
#endif
#endif /* __XFS_QM_H__ */

View File

@@ -0,0 +1,187 @@
/*
* Copyright (c) 2000-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_bit.h"
#include "xfs_log.h"
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_quota.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_ialloc.h"
#include "xfs_itable.h"
#include "xfs_btree.h"
#include "xfs_bmap.h"
#include "xfs_rtalloc.h"
#include "xfs_error.h"
#include "xfs_rw.h"
#include "xfs_attr.h"
#include "xfs_buf_item.h"
#include "xfs_qm.h"
STATIC void
xfs_fill_statvfs_from_dquot(
struct kstatfs *statp,
xfs_disk_dquot_t *dp)
{
__uint64_t limit;
limit = dp->d_blk_softlimit ?
be64_to_cpu(dp->d_blk_softlimit) :
be64_to_cpu(dp->d_blk_hardlimit);
if (limit && statp->f_blocks > limit) {
statp->f_blocks = limit;
statp->f_bfree = statp->f_bavail =
(statp->f_blocks > be64_to_cpu(dp->d_bcount)) ?
(statp->f_blocks - be64_to_cpu(dp->d_bcount)) : 0;
}
limit = dp->d_ino_softlimit ?
be64_to_cpu(dp->d_ino_softlimit) :
be64_to_cpu(dp->d_ino_hardlimit);
if (limit && statp->f_files > limit) {
statp->f_files = limit;
statp->f_ffree =
(statp->f_files > be64_to_cpu(dp->d_icount)) ?
(statp->f_ffree - be64_to_cpu(dp->d_icount)) : 0;
}
}
/*
* Directory tree accounting is implemented using project quotas, where
* the project identifier is inherited from parent directories.
* A statvfs (df, etc.) of a directory that is using project quota should
* return a statvfs of the project, not the entire filesystem.
* This makes such trees appear as if they are filesystems in themselves.
*/
void
xfs_qm_statvfs(
xfs_inode_t *ip,
struct kstatfs *statp)
{
xfs_mount_t *mp = ip->i_mount;
xfs_dquot_t *dqp;
if (!xfs_qm_dqget(mp, NULL, ip->i_d.di_projid, XFS_DQ_PROJ, 0, &dqp)) {
xfs_fill_statvfs_from_dquot(statp, &dqp->q_core);
xfs_qm_dqput(dqp);
}
}
int
xfs_qm_newmount(
xfs_mount_t *mp,
uint *needquotamount,
uint *quotaflags)
{
uint quotaondisk;
uint uquotaondisk = 0, gquotaondisk = 0, pquotaondisk = 0;
quotaondisk = xfs_sb_version_hasquota(&mp->m_sb) &&
(mp->m_sb.sb_qflags & XFS_ALL_QUOTA_ACCT);
if (quotaondisk) {
uquotaondisk = mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT;
pquotaondisk = mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT;
gquotaondisk = mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT;
}
/*
* If the device itself is read-only, we can't allow
* the user to change the state of quota on the mount -
* this would generate a transaction on the ro device,
* which would lead to an I/O error and shutdown
*/
if (((uquotaondisk && !XFS_IS_UQUOTA_ON(mp)) ||
(!uquotaondisk && XFS_IS_UQUOTA_ON(mp)) ||
(pquotaondisk && !XFS_IS_PQUOTA_ON(mp)) ||
(!pquotaondisk && XFS_IS_PQUOTA_ON(mp)) ||
(gquotaondisk && !XFS_IS_GQUOTA_ON(mp)) ||
(!gquotaondisk && XFS_IS_OQUOTA_ON(mp))) &&
xfs_dev_is_read_only(mp, "changing quota state")) {
cmn_err(CE_WARN,
"XFS: please mount with%s%s%s%s.",
(!quotaondisk ? "out quota" : ""),
(uquotaondisk ? " usrquota" : ""),
(pquotaondisk ? " prjquota" : ""),
(gquotaondisk ? " grpquota" : ""));
return XFS_ERROR(EPERM);
}
if (XFS_IS_QUOTA_ON(mp) || quotaondisk) {
/*
* Call mount_quotas at this point only if we won't have to do
* a quotacheck.
*/
if (quotaondisk && !XFS_QM_NEED_QUOTACHECK(mp)) {
/*
* If an error occured, qm_mount_quotas code
* has already disabled quotas. So, just finish
* mounting, and get on with the boring life
* without disk quotas.
*/
xfs_qm_mount_quotas(mp);
} else {
/*
* Clear the quota flags, but remember them. This
* is so that the quota code doesn't get invoked
* before we're ready. This can happen when an
* inode goes inactive and wants to free blocks,
* or via xfs_log_mount_finish.
*/
*needquotamount = B_TRUE;
*quotaflags = mp->m_qflags;
mp->m_qflags = 0;
}
}
return 0;
}
void __init
xfs_qm_init(void)
{
printk(KERN_INFO "SGI XFS Quota Management subsystem\n");
mutex_init(&xfs_Gqm_lock);
xfs_qm_init_procfs();
}
void __exit
xfs_qm_exit(void)
{
xfs_qm_cleanup_procfs();
if (qm_dqzone)
kmem_zone_destroy(qm_dqzone);
if (qm_dqtrxzone)
kmem_zone_destroy(qm_dqtrxzone);
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (c) 2000-2003 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_bit.h"
#include "xfs_log.h"
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_quota.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_ialloc.h"
#include "xfs_itable.h"
#include "xfs_bmap.h"
#include "xfs_btree.h"
#include "xfs_rtalloc.h"
#include "xfs_error.h"
#include "xfs_rw.h"
#include "xfs_attr.h"
#include "xfs_buf_item.h"
#include "xfs_qm.h"
struct xqmstats xqmstats;
static int xqm_proc_show(struct seq_file *m, void *v)
{
/* maximum; incore; ratio free to inuse; freelist */
seq_printf(m, "%d\t%d\t%d\t%u\n",
ndquot,
xfs_Gqm? atomic_read(&xfs_Gqm->qm_totaldquots) : 0,
xfs_Gqm? xfs_Gqm->qm_dqfree_ratio : 0,
xfs_Gqm? xfs_Gqm->qm_dqfreelist.qh_nelems : 0);
return 0;
}
static int xqm_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, xqm_proc_show, NULL);
}
static const struct file_operations xqm_proc_fops = {
.owner = THIS_MODULE,
.open = xqm_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int xqmstat_proc_show(struct seq_file *m, void *v)
{
/* quota performance statistics */
seq_printf(m, "qm %u %u %u %u %u %u %u %u\n",
xqmstats.xs_qm_dqreclaims,
xqmstats.xs_qm_dqreclaim_misses,
xqmstats.xs_qm_dquot_dups,
xqmstats.xs_qm_dqcachemisses,
xqmstats.xs_qm_dqcachehits,
xqmstats.xs_qm_dqwants,
xqmstats.xs_qm_dqshake_reclaims,
xqmstats.xs_qm_dqinact_reclaims);
return 0;
}
static int xqmstat_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, xqmstat_proc_show, NULL);
}
static const struct file_operations xqmstat_proc_fops = {
.owner = THIS_MODULE,
.open = xqmstat_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
void
xfs_qm_init_procfs(void)
{
proc_create("fs/xfs/xqmstat", 0, NULL, &xqmstat_proc_fops);
proc_create("fs/xfs/xqm", 0, NULL, &xqm_proc_fops);
}
void
xfs_qm_cleanup_procfs(void)
{
remove_proc_entry("fs/xfs/xqm", NULL);
remove_proc_entry("fs/xfs/xqmstat", NULL);
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2002 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __XFS_QM_STATS_H__
#define __XFS_QM_STATS_H__
#if defined(CONFIG_PROC_FS) && !defined(XFS_STATS_OFF)
/*
* XQM global statistics
*/
struct xqmstats {
__uint32_t xs_qm_dqreclaims;
__uint32_t xs_qm_dqreclaim_misses;
__uint32_t xs_qm_dquot_dups;
__uint32_t xs_qm_dqcachemisses;
__uint32_t xs_qm_dqcachehits;
__uint32_t xs_qm_dqwants;
__uint32_t xs_qm_dqshake_reclaims;
__uint32_t xs_qm_dqinact_reclaims;
};
extern struct xqmstats xqmstats;
# define XQM_STATS_INC(count) ( (count)++ )
extern void xfs_qm_init_procfs(void);
extern void xfs_qm_cleanup_procfs(void);
#else
# define XQM_STATS_INC(count) do { } while (0)
static inline void xfs_qm_init_procfs(void) { };
static inline void xfs_qm_cleanup_procfs(void) { };
#endif
#endif /* __XFS_QM_STATS_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,155 @@
/*
* Copyright (c) 2000-2003 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __XFS_QUOTA_PRIV_H__
#define __XFS_QUOTA_PRIV_H__
/*
* Number of bmaps that we ask from bmapi when doing a quotacheck.
* We make this restriction to keep the memory usage to a minimum.
*/
#define XFS_DQITER_MAP_SIZE 10
/* Number of dquots that fit in to a dquot block */
#define XFS_QM_DQPERBLK(mp) ((mp)->m_quotainfo->qi_dqperchunk)
#define XFS_DQ_IS_ADDEDTO_TRX(t, d) ((d)->q_transp == (t))
#define XFS_QI_MPLRECLAIMS(mp) ((mp)->m_quotainfo->qi_dqreclaims)
#define XFS_QI_UQIP(mp) ((mp)->m_quotainfo->qi_uquotaip)
#define XFS_QI_GQIP(mp) ((mp)->m_quotainfo->qi_gquotaip)
#define XFS_QI_DQCHUNKLEN(mp) ((mp)->m_quotainfo->qi_dqchunklen)
#define XFS_QI_BTIMELIMIT(mp) ((mp)->m_quotainfo->qi_btimelimit)
#define XFS_QI_RTBTIMELIMIT(mp) ((mp)->m_quotainfo->qi_rtbtimelimit)
#define XFS_QI_ITIMELIMIT(mp) ((mp)->m_quotainfo->qi_itimelimit)
#define XFS_QI_BWARNLIMIT(mp) ((mp)->m_quotainfo->qi_bwarnlimit)
#define XFS_QI_RTBWARNLIMIT(mp) ((mp)->m_quotainfo->qi_rtbwarnlimit)
#define XFS_QI_IWARNLIMIT(mp) ((mp)->m_quotainfo->qi_iwarnlimit)
#define XFS_QI_QOFFLOCK(mp) ((mp)->m_quotainfo->qi_quotaofflock)
#define XFS_QI_MPL_LIST(mp) ((mp)->m_quotainfo->qi_dqlist)
#define XFS_QI_MPLNEXT(mp) ((mp)->m_quotainfo->qi_dqlist.qh_next)
#define XFS_QI_MPLNDQUOTS(mp) ((mp)->m_quotainfo->qi_dqlist.qh_nelems)
#define xfs_qm_mplist_lock(mp) \
mutex_lock(&(XFS_QI_MPL_LIST(mp).qh_lock))
#define xfs_qm_mplist_nowait(mp) \
mutex_trylock(&(XFS_QI_MPL_LIST(mp).qh_lock))
#define xfs_qm_mplist_unlock(mp) \
mutex_unlock(&(XFS_QI_MPL_LIST(mp).qh_lock))
#define XFS_QM_IS_MPLIST_LOCKED(mp) \
mutex_is_locked(&(XFS_QI_MPL_LIST(mp).qh_lock))
#define xfs_qm_freelist_lock(qm) \
mutex_lock(&((qm)->qm_dqfreelist.qh_lock))
#define xfs_qm_freelist_lock_nowait(qm) \
mutex_trylock(&((qm)->qm_dqfreelist.qh_lock))
#define xfs_qm_freelist_unlock(qm) \
mutex_unlock(&((qm)->qm_dqfreelist.qh_lock))
/*
* Hash into a bucket in the dquot hash table, based on <mp, id>.
*/
#define XFS_DQ_HASHVAL(mp, id) (((__psunsigned_t)(mp) + \
(__psunsigned_t)(id)) & \
(xfs_Gqm->qm_dqhashmask - 1))
#define XFS_DQ_HASH(mp, id, type) (type == XFS_DQ_USER ? \
(xfs_Gqm->qm_usr_dqhtable + \
XFS_DQ_HASHVAL(mp, id)) : \
(xfs_Gqm->qm_grp_dqhtable + \
XFS_DQ_HASHVAL(mp, id)))
#define XFS_IS_DQTYPE_ON(mp, type) (type == XFS_DQ_USER ? \
XFS_IS_UQUOTA_ON(mp) : \
XFS_IS_OQUOTA_ON(mp))
#define XFS_IS_DQUOT_UNINITIALIZED(dqp) ( \
!dqp->q_core.d_blk_hardlimit && \
!dqp->q_core.d_blk_softlimit && \
!dqp->q_core.d_rtb_hardlimit && \
!dqp->q_core.d_rtb_softlimit && \
!dqp->q_core.d_ino_hardlimit && \
!dqp->q_core.d_ino_softlimit && \
!dqp->q_core.d_bcount && \
!dqp->q_core.d_rtbcount && \
!dqp->q_core.d_icount)
#define HL_PREVP dq_hashlist.ql_prevp
#define HL_NEXT dq_hashlist.ql_next
#define MPL_PREVP dq_mplist.ql_prevp
#define MPL_NEXT dq_mplist.ql_next
#define _LIST_REMOVE(h, dqp, PVP, NXT) \
{ \
xfs_dquot_t *d; \
if (((d) = (dqp)->NXT)) \
(d)->PVP = (dqp)->PVP; \
*((dqp)->PVP) = d; \
(dqp)->NXT = NULL; \
(dqp)->PVP = NULL; \
(h)->qh_version++; \
(h)->qh_nelems--; \
}
#define _LIST_INSERT(h, dqp, PVP, NXT) \
{ \
xfs_dquot_t *d; \
if (((d) = (h)->qh_next)) \
(d)->PVP = &((dqp)->NXT); \
(dqp)->NXT = d; \
(dqp)->PVP = &((h)->qh_next); \
(h)->qh_next = dqp; \
(h)->qh_version++; \
(h)->qh_nelems++; \
}
#define FOREACH_DQUOT_IN_MP(dqp, mp) \
for ((dqp) = XFS_QI_MPLNEXT(mp); (dqp) != NULL; (dqp) = (dqp)->MPL_NEXT)
#define FOREACH_DQUOT_IN_FREELIST(dqp, qlist) \
for ((dqp) = (qlist)->qh_next; (dqp) != (xfs_dquot_t *)(qlist); \
(dqp) = (dqp)->dq_flnext)
#define XQM_HASHLIST_INSERT(h, dqp) \
_LIST_INSERT(h, dqp, HL_PREVP, HL_NEXT)
#define XQM_FREELIST_INSERT(h, dqp) \
xfs_qm_freelist_append(h, dqp)
#define XQM_MPLIST_INSERT(h, dqp) \
_LIST_INSERT(h, dqp, MPL_PREVP, MPL_NEXT)
#define XQM_HASHLIST_REMOVE(h, dqp) \
_LIST_REMOVE(h, dqp, HL_PREVP, HL_NEXT)
#define XQM_FREELIST_REMOVE(dqp) \
xfs_qm_freelist_unlink(dqp)
#define XQM_MPLIST_REMOVE(h, dqp) \
{ _LIST_REMOVE(h, dqp, MPL_PREVP, MPL_NEXT); \
XFS_QI_MPLRECLAIMS((dqp)->q_mount)++; }
#define XFS_DQ_IS_LOGITEM_INITD(dqp) ((dqp)->q_logitem.qli_dquot == (dqp))
#define XFS_QM_DQP_TO_DQACCT(tp, dqp) (XFS_QM_ISUDQ(dqp) ? \
(tp)->t_dqinfo->dqa_usrdquots : \
(tp)->t_dqinfo->dqa_grpdquots)
#define XFS_IS_SUSER_DQUOT(dqp) \
(!((dqp)->q_core.d_id))
#define DQFLAGTO_TYPESTR(d) (((d)->dq_flags & XFS_DQ_USER) ? "USR" : \
(((d)->dq_flags & XFS_DQ_GROUP) ? "GRP" : \
(((d)->dq_flags & XFS_DQ_PROJ) ? "PRJ":"???")))
#endif /* __XFS_QUOTA_PRIV_H__ */

View File

@@ -0,0 +1,903 @@
/*
* Copyright (c) 2000-2002 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_bit.h"
#include "xfs_log.h"
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_quota.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
#include "xfs_attr_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_ialloc.h"
#include "xfs_itable.h"
#include "xfs_btree.h"
#include "xfs_bmap.h"
#include "xfs_rtalloc.h"
#include "xfs_error.h"
#include "xfs_rw.h"
#include "xfs_attr.h"
#include "xfs_buf_item.h"
#include "xfs_trans_priv.h"
#include "xfs_qm.h"
STATIC void xfs_trans_alloc_dqinfo(xfs_trans_t *);
/*
* Add the locked dquot to the transaction.
* The dquot must be locked, and it cannot be associated with any
* transaction.
*/
void
xfs_trans_dqjoin(
xfs_trans_t *tp,
xfs_dquot_t *dqp)
{
xfs_dq_logitem_t *lp;
ASSERT(! XFS_DQ_IS_ADDEDTO_TRX(tp, dqp));
ASSERT(XFS_DQ_IS_LOCKED(dqp));
ASSERT(XFS_DQ_IS_LOGITEM_INITD(dqp));
lp = &dqp->q_logitem;
/*
* Get a log_item_desc to point at the new item.
*/
(void) xfs_trans_add_item(tp, (xfs_log_item_t*)(lp));
/*
* Initialize i_transp so we can later determine if this dquot is
* associated with this transaction.
*/
dqp->q_transp = tp;
}
/*
* This is called to mark the dquot as needing
* to be logged when the transaction is committed. The dquot must
* already be associated with the given transaction.
* Note that it marks the entire transaction as dirty. In the ordinary
* case, this gets called via xfs_trans_commit, after the transaction
* is already dirty. However, there's nothing stop this from getting
* called directly, as done by xfs_qm_scall_setqlim. Hence, the TRANS_DIRTY
* flag.
*/
void
xfs_trans_log_dquot(
xfs_trans_t *tp,
xfs_dquot_t *dqp)
{
xfs_log_item_desc_t *lidp;
ASSERT(XFS_DQ_IS_ADDEDTO_TRX(tp, dqp));
ASSERT(XFS_DQ_IS_LOCKED(dqp));
lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)(&dqp->q_logitem));
ASSERT(lidp != NULL);
tp->t_flags |= XFS_TRANS_DIRTY;
lidp->lid_flags |= XFS_LID_DIRTY;
}
/*
* Carry forward whatever is left of the quota blk reservation to
* the spanky new transaction
*/
void
xfs_trans_dup_dqinfo(
xfs_trans_t *otp,
xfs_trans_t *ntp)
{
xfs_dqtrx_t *oq, *nq;
int i,j;
xfs_dqtrx_t *oqa, *nqa;
if (!otp->t_dqinfo)
return;
xfs_trans_alloc_dqinfo(ntp);
oqa = otp->t_dqinfo->dqa_usrdquots;
nqa = ntp->t_dqinfo->dqa_usrdquots;
/*
* Because the quota blk reservation is carried forward,
* it is also necessary to carry forward the DQ_DIRTY flag.
*/
if(otp->t_flags & XFS_TRANS_DQ_DIRTY)
ntp->t_flags |= XFS_TRANS_DQ_DIRTY;
for (j = 0; j < 2; j++) {
for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
if (oqa[i].qt_dquot == NULL)
break;
oq = &oqa[i];
nq = &nqa[i];
nq->qt_dquot = oq->qt_dquot;
nq->qt_bcount_delta = nq->qt_icount_delta = 0;
nq->qt_rtbcount_delta = 0;
/*
* Transfer whatever is left of the reservations.
*/
nq->qt_blk_res = oq->qt_blk_res - oq->qt_blk_res_used;
oq->qt_blk_res = oq->qt_blk_res_used;
nq->qt_rtblk_res = oq->qt_rtblk_res -
oq->qt_rtblk_res_used;
oq->qt_rtblk_res = oq->qt_rtblk_res_used;
nq->qt_ino_res = oq->qt_ino_res - oq->qt_ino_res_used;
oq->qt_ino_res = oq->qt_ino_res_used;
}
oqa = otp->t_dqinfo->dqa_grpdquots;
nqa = ntp->t_dqinfo->dqa_grpdquots;
}
}
/*
* Wrap around mod_dquot to account for both user and group quotas.
*/
void
xfs_trans_mod_dquot_byino(
xfs_trans_t *tp,
xfs_inode_t *ip,
uint field,
long delta)
{
xfs_mount_t *mp = tp->t_mountp;
if (!XFS_IS_QUOTA_RUNNING(mp) ||
!XFS_IS_QUOTA_ON(mp) ||
ip->i_ino == mp->m_sb.sb_uquotino ||
ip->i_ino == mp->m_sb.sb_gquotino)
return;
if (tp->t_dqinfo == NULL)
xfs_trans_alloc_dqinfo(tp);
if (XFS_IS_UQUOTA_ON(mp) && ip->i_udquot)
(void) xfs_trans_mod_dquot(tp, ip->i_udquot, field, delta);
if (XFS_IS_OQUOTA_ON(mp) && ip->i_gdquot)
(void) xfs_trans_mod_dquot(tp, ip->i_gdquot, field, delta);
}
STATIC xfs_dqtrx_t *
xfs_trans_get_dqtrx(
xfs_trans_t *tp,
xfs_dquot_t *dqp)
{
int i;
xfs_dqtrx_t *qa;
for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
qa = XFS_QM_DQP_TO_DQACCT(tp, dqp);
if (qa[i].qt_dquot == NULL ||
qa[i].qt_dquot == dqp) {
return (&qa[i]);
}
}
return (NULL);
}
/*
* Make the changes in the transaction structure.
* The moral equivalent to xfs_trans_mod_sb().
* We don't touch any fields in the dquot, so we don't care
* if it's locked or not (most of the time it won't be).
*/
void
xfs_trans_mod_dquot(
xfs_trans_t *tp,
xfs_dquot_t *dqp,
uint field,
long delta)
{
xfs_dqtrx_t *qtrx;
ASSERT(tp);
ASSERT(XFS_IS_QUOTA_RUNNING(tp->t_mountp));
qtrx = NULL;
if (tp->t_dqinfo == NULL)
xfs_trans_alloc_dqinfo(tp);
/*
* Find either the first free slot or the slot that belongs
* to this dquot.
*/
qtrx = xfs_trans_get_dqtrx(tp, dqp);
ASSERT(qtrx);
if (qtrx->qt_dquot == NULL)
qtrx->qt_dquot = dqp;
switch (field) {
/*
* regular disk blk reservation
*/
case XFS_TRANS_DQ_RES_BLKS:
qtrx->qt_blk_res += (ulong)delta;
break;
/*
* inode reservation
*/
case XFS_TRANS_DQ_RES_INOS:
qtrx->qt_ino_res += (ulong)delta;
break;
/*
* disk blocks used.
*/
case XFS_TRANS_DQ_BCOUNT:
if (qtrx->qt_blk_res && delta > 0) {
qtrx->qt_blk_res_used += (ulong)delta;
ASSERT(qtrx->qt_blk_res >= qtrx->qt_blk_res_used);
}
qtrx->qt_bcount_delta += delta;
break;
case XFS_TRANS_DQ_DELBCOUNT:
qtrx->qt_delbcnt_delta += delta;
break;
/*
* Inode Count
*/
case XFS_TRANS_DQ_ICOUNT:
if (qtrx->qt_ino_res && delta > 0) {
qtrx->qt_ino_res_used += (ulong)delta;
ASSERT(qtrx->qt_ino_res >= qtrx->qt_ino_res_used);
}
qtrx->qt_icount_delta += delta;
break;
/*
* rtblk reservation
*/
case XFS_TRANS_DQ_RES_RTBLKS:
qtrx->qt_rtblk_res += (ulong)delta;
break;
/*
* rtblk count
*/
case XFS_TRANS_DQ_RTBCOUNT:
if (qtrx->qt_rtblk_res && delta > 0) {
qtrx->qt_rtblk_res_used += (ulong)delta;
ASSERT(qtrx->qt_rtblk_res >= qtrx->qt_rtblk_res_used);
}
qtrx->qt_rtbcount_delta += delta;
break;
case XFS_TRANS_DQ_DELRTBCOUNT:
qtrx->qt_delrtb_delta += delta;
break;
default:
ASSERT(0);
}
tp->t_flags |= XFS_TRANS_DQ_DIRTY;
}
/*
* Given an array of dqtrx structures, lock all the dquots associated
* and join them to the transaction, provided they have been modified.
* We know that the highest number of dquots (of one type - usr OR grp),
* involved in a transaction is 2 and that both usr and grp combined - 3.
* So, we don't attempt to make this very generic.
*/
STATIC void
xfs_trans_dqlockedjoin(
xfs_trans_t *tp,
xfs_dqtrx_t *q)
{
ASSERT(q[0].qt_dquot != NULL);
if (q[1].qt_dquot == NULL) {
xfs_dqlock(q[0].qt_dquot);
xfs_trans_dqjoin(tp, q[0].qt_dquot);
} else {
ASSERT(XFS_QM_TRANS_MAXDQS == 2);
xfs_dqlock2(q[0].qt_dquot, q[1].qt_dquot);
xfs_trans_dqjoin(tp, q[0].qt_dquot);
xfs_trans_dqjoin(tp, q[1].qt_dquot);
}
}
/*
* Called by xfs_trans_commit() and similar in spirit to
* xfs_trans_apply_sb_deltas().
* Go thru all the dquots belonging to this transaction and modify the
* INCORE dquot to reflect the actual usages.
* Unreserve just the reservations done by this transaction.
* dquot is still left locked at exit.
*/
void
xfs_trans_apply_dquot_deltas(
xfs_trans_t *tp)
{
int i, j;
xfs_dquot_t *dqp;
xfs_dqtrx_t *qtrx, *qa;
xfs_disk_dquot_t *d;
long totalbdelta;
long totalrtbdelta;
if (!(tp->t_flags & XFS_TRANS_DQ_DIRTY))
return;
ASSERT(tp->t_dqinfo);
qa = tp->t_dqinfo->dqa_usrdquots;
for (j = 0; j < 2; j++) {
if (qa[0].qt_dquot == NULL) {
qa = tp->t_dqinfo->dqa_grpdquots;
continue;
}
/*
* Lock all of the dquots and join them to the transaction.
*/
xfs_trans_dqlockedjoin(tp, qa);
for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
qtrx = &qa[i];
/*
* The array of dquots is filled
* sequentially, not sparsely.
*/
if ((dqp = qtrx->qt_dquot) == NULL)
break;
ASSERT(XFS_DQ_IS_LOCKED(dqp));
ASSERT(XFS_DQ_IS_ADDEDTO_TRX(tp, dqp));
/*
* adjust the actual number of blocks used
*/
d = &dqp->q_core;
/*
* The issue here is - sometimes we don't make a blkquota
* reservation intentionally to be fair to users
* (when the amount is small). On the other hand,
* delayed allocs do make reservations, but that's
* outside of a transaction, so we have no
* idea how much was really reserved.
* So, here we've accumulated delayed allocation blks and
* non-delay blks. The assumption is that the
* delayed ones are always reserved (outside of a
* transaction), and the others may or may not have
* quota reservations.
*/
totalbdelta = qtrx->qt_bcount_delta +
qtrx->qt_delbcnt_delta;
totalrtbdelta = qtrx->qt_rtbcount_delta +
qtrx->qt_delrtb_delta;
#ifdef QUOTADEBUG
if (totalbdelta < 0)
ASSERT(be64_to_cpu(d->d_bcount) >=
(xfs_qcnt_t) -totalbdelta);
if (totalrtbdelta < 0)
ASSERT(be64_to_cpu(d->d_rtbcount) >=
(xfs_qcnt_t) -totalrtbdelta);
if (qtrx->qt_icount_delta < 0)
ASSERT(be64_to_cpu(d->d_icount) >=
(xfs_qcnt_t) -qtrx->qt_icount_delta);
#endif
if (totalbdelta)
be64_add_cpu(&d->d_bcount, (xfs_qcnt_t)totalbdelta);
if (qtrx->qt_icount_delta)
be64_add_cpu(&d->d_icount, (xfs_qcnt_t)qtrx->qt_icount_delta);
if (totalrtbdelta)
be64_add_cpu(&d->d_rtbcount, (xfs_qcnt_t)totalrtbdelta);
/*
* Get any default limits in use.
* Start/reset the timer(s) if needed.
*/
if (d->d_id) {
xfs_qm_adjust_dqlimits(tp->t_mountp, d);
xfs_qm_adjust_dqtimers(tp->t_mountp, d);
}
dqp->dq_flags |= XFS_DQ_DIRTY;
/*
* add this to the list of items to get logged
*/
xfs_trans_log_dquot(tp, dqp);
/*
* Take off what's left of the original reservation.
* In case of delayed allocations, there's no
* reservation that a transaction structure knows of.
*/
if (qtrx->qt_blk_res != 0) {
if (qtrx->qt_blk_res != qtrx->qt_blk_res_used) {
if (qtrx->qt_blk_res >
qtrx->qt_blk_res_used)
dqp->q_res_bcount -= (xfs_qcnt_t)
(qtrx->qt_blk_res -
qtrx->qt_blk_res_used);
else
dqp->q_res_bcount -= (xfs_qcnt_t)
(qtrx->qt_blk_res_used -
qtrx->qt_blk_res);
}
} else {
/*
* These blks were never reserved, either inside
* a transaction or outside one (in a delayed
* allocation). Also, this isn't always a
* negative number since we sometimes
* deliberately skip quota reservations.
*/
if (qtrx->qt_bcount_delta) {
dqp->q_res_bcount +=
(xfs_qcnt_t)qtrx->qt_bcount_delta;
}
}
/*
* Adjust the RT reservation.
*/
if (qtrx->qt_rtblk_res != 0) {
if (qtrx->qt_rtblk_res != qtrx->qt_rtblk_res_used) {
if (qtrx->qt_rtblk_res >
qtrx->qt_rtblk_res_used)
dqp->q_res_rtbcount -= (xfs_qcnt_t)
(qtrx->qt_rtblk_res -
qtrx->qt_rtblk_res_used);
else
dqp->q_res_rtbcount -= (xfs_qcnt_t)
(qtrx->qt_rtblk_res_used -
qtrx->qt_rtblk_res);
}
} else {
if (qtrx->qt_rtbcount_delta)
dqp->q_res_rtbcount +=
(xfs_qcnt_t)qtrx->qt_rtbcount_delta;
}
/*
* Adjust the inode reservation.
*/
if (qtrx->qt_ino_res != 0) {
ASSERT(qtrx->qt_ino_res >=
qtrx->qt_ino_res_used);
if (qtrx->qt_ino_res > qtrx->qt_ino_res_used)
dqp->q_res_icount -= (xfs_qcnt_t)
(qtrx->qt_ino_res -
qtrx->qt_ino_res_used);
} else {
if (qtrx->qt_icount_delta)
dqp->q_res_icount +=
(xfs_qcnt_t)qtrx->qt_icount_delta;
}
ASSERT(dqp->q_res_bcount >=
be64_to_cpu(dqp->q_core.d_bcount));
ASSERT(dqp->q_res_icount >=
be64_to_cpu(dqp->q_core.d_icount));
ASSERT(dqp->q_res_rtbcount >=
be64_to_cpu(dqp->q_core.d_rtbcount));
}
/*
* Do the group quotas next
*/
qa = tp->t_dqinfo->dqa_grpdquots;
}
}
/*
* Release the reservations, and adjust the dquots accordingly.
* This is called only when the transaction is being aborted. If by
* any chance we have done dquot modifications incore (ie. deltas) already,
* we simply throw those away, since that's the expected behavior
* when a transaction is curtailed without a commit.
*/
void
xfs_trans_unreserve_and_mod_dquots(
xfs_trans_t *tp)
{
int i, j;
xfs_dquot_t *dqp;
xfs_dqtrx_t *qtrx, *qa;
boolean_t locked;
if (!tp->t_dqinfo || !(tp->t_flags & XFS_TRANS_DQ_DIRTY))
return;
qa = tp->t_dqinfo->dqa_usrdquots;
for (j = 0; j < 2; j++) {
for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
qtrx = &qa[i];
/*
* We assume that the array of dquots is filled
* sequentially, not sparsely.
*/
if ((dqp = qtrx->qt_dquot) == NULL)
break;
/*
* Unreserve the original reservation. We don't care
* about the number of blocks used field, or deltas.
* Also we don't bother to zero the fields.
*/
locked = B_FALSE;
if (qtrx->qt_blk_res) {
xfs_dqlock(dqp);
locked = B_TRUE;
dqp->q_res_bcount -=
(xfs_qcnt_t)qtrx->qt_blk_res;
}
if (qtrx->qt_ino_res) {
if (!locked) {
xfs_dqlock(dqp);
locked = B_TRUE;
}
dqp->q_res_icount -=
(xfs_qcnt_t)qtrx->qt_ino_res;
}
if (qtrx->qt_rtblk_res) {
if (!locked) {
xfs_dqlock(dqp);
locked = B_TRUE;
}
dqp->q_res_rtbcount -=
(xfs_qcnt_t)qtrx->qt_rtblk_res;
}
if (locked)
xfs_dqunlock(dqp);
}
qa = tp->t_dqinfo->dqa_grpdquots;
}
}
STATIC int
xfs_quota_error(uint flags)
{
if (flags & XFS_QMOPT_ENOSPC)
return ENOSPC;
return EDQUOT;
}
/*
* This reserves disk blocks and inodes against a dquot.
* Flags indicate if the dquot is to be locked here and also
* if the blk reservation is for RT or regular blocks.
* Sending in XFS_QMOPT_FORCE_RES flag skips the quota check.
*/
STATIC int
xfs_trans_dqresv(
xfs_trans_t *tp,
xfs_mount_t *mp,
xfs_dquot_t *dqp,
long nblks,
long ninos,
uint flags)
{
int error;
xfs_qcnt_t hardlimit;
xfs_qcnt_t softlimit;
time_t timer;
xfs_qwarncnt_t warns;
xfs_qwarncnt_t warnlimit;
xfs_qcnt_t count;
xfs_qcnt_t *resbcountp;
xfs_quotainfo_t *q = mp->m_quotainfo;
xfs_dqlock(dqp);
if (flags & XFS_TRANS_DQ_RES_BLKS) {
hardlimit = be64_to_cpu(dqp->q_core.d_blk_hardlimit);
if (!hardlimit)
hardlimit = q->qi_bhardlimit;
softlimit = be64_to_cpu(dqp->q_core.d_blk_softlimit);
if (!softlimit)
softlimit = q->qi_bsoftlimit;
timer = be32_to_cpu(dqp->q_core.d_btimer);
warns = be16_to_cpu(dqp->q_core.d_bwarns);
warnlimit = XFS_QI_BWARNLIMIT(dqp->q_mount);
resbcountp = &dqp->q_res_bcount;
} else {
ASSERT(flags & XFS_TRANS_DQ_RES_RTBLKS);
hardlimit = be64_to_cpu(dqp->q_core.d_rtb_hardlimit);
if (!hardlimit)
hardlimit = q->qi_rtbhardlimit;
softlimit = be64_to_cpu(dqp->q_core.d_rtb_softlimit);
if (!softlimit)
softlimit = q->qi_rtbsoftlimit;
timer = be32_to_cpu(dqp->q_core.d_rtbtimer);
warns = be16_to_cpu(dqp->q_core.d_rtbwarns);
warnlimit = XFS_QI_RTBWARNLIMIT(dqp->q_mount);
resbcountp = &dqp->q_res_rtbcount;
}
error = 0;
if ((flags & XFS_QMOPT_FORCE_RES) == 0 &&
dqp->q_core.d_id &&
((XFS_IS_UQUOTA_ENFORCED(dqp->q_mount) && XFS_QM_ISUDQ(dqp)) ||
(XFS_IS_OQUOTA_ENFORCED(dqp->q_mount) &&
(XFS_QM_ISPDQ(dqp) || XFS_QM_ISGDQ(dqp))))) {
#ifdef QUOTADEBUG
cmn_err(CE_DEBUG, "BLK Res: nblks=%ld + resbcount=%Ld"
" > hardlimit=%Ld?", nblks, *resbcountp, hardlimit);
#endif
if (nblks > 0) {
/*
* dquot is locked already. See if we'd go over the
* hardlimit or exceed the timelimit if we allocate
* nblks.
*/
if (hardlimit > 0ULL &&
(hardlimit <= nblks + *resbcountp)) {
error = xfs_quota_error(flags);
goto error_return;
}
if (softlimit > 0ULL &&
(softlimit <= nblks + *resbcountp)) {
if ((timer != 0 && get_seconds() > timer) ||
(warns != 0 && warns >= warnlimit)) {
error = xfs_quota_error(flags);
goto error_return;
}
}
}
if (ninos > 0) {
count = be64_to_cpu(dqp->q_core.d_icount);
timer = be32_to_cpu(dqp->q_core.d_itimer);
warns = be16_to_cpu(dqp->q_core.d_iwarns);
warnlimit = XFS_QI_IWARNLIMIT(dqp->q_mount);
hardlimit = be64_to_cpu(dqp->q_core.d_ino_hardlimit);
if (!hardlimit)
hardlimit = q->qi_ihardlimit;
softlimit = be64_to_cpu(dqp->q_core.d_ino_softlimit);
if (!softlimit)
softlimit = q->qi_isoftlimit;
if (hardlimit > 0ULL && count >= hardlimit) {
error = xfs_quota_error(flags);
goto error_return;
} else if (softlimit > 0ULL && count >= softlimit) {
if ((timer != 0 && get_seconds() > timer) ||
(warns != 0 && warns >= warnlimit)) {
error = xfs_quota_error(flags);
goto error_return;
}
}
}
}
/*
* Change the reservation, but not the actual usage.
* Note that q_res_bcount = q_core.d_bcount + resv
*/
(*resbcountp) += (xfs_qcnt_t)nblks;
if (ninos != 0)
dqp->q_res_icount += (xfs_qcnt_t)ninos;
/*
* note the reservation amt in the trans struct too,
* so that the transaction knows how much was reserved by
* it against this particular dquot.
* We don't do this when we are reserving for a delayed allocation,
* because we don't have the luxury of a transaction envelope then.
*/
if (tp) {
ASSERT(tp->t_dqinfo);
ASSERT(flags & XFS_QMOPT_RESBLK_MASK);
if (nblks != 0)
xfs_trans_mod_dquot(tp, dqp,
flags & XFS_QMOPT_RESBLK_MASK,
nblks);
if (ninos != 0)
xfs_trans_mod_dquot(tp, dqp,
XFS_TRANS_DQ_RES_INOS,
ninos);
}
ASSERT(dqp->q_res_bcount >= be64_to_cpu(dqp->q_core.d_bcount));
ASSERT(dqp->q_res_rtbcount >= be64_to_cpu(dqp->q_core.d_rtbcount));
ASSERT(dqp->q_res_icount >= be64_to_cpu(dqp->q_core.d_icount));
error_return:
xfs_dqunlock(dqp);
return error;
}
/*
* Given dquot(s), make disk block and/or inode reservations against them.
* The fact that this does the reservation against both the usr and
* grp/prj quotas is important, because this follows a both-or-nothing
* approach.
*
* flags = XFS_QMOPT_FORCE_RES evades limit enforcement. Used by chown.
* XFS_QMOPT_ENOSPC returns ENOSPC not EDQUOT. Used by pquota.
* XFS_TRANS_DQ_RES_BLKS reserves regular disk blocks
* XFS_TRANS_DQ_RES_RTBLKS reserves realtime disk blocks
* dquots are unlocked on return, if they were not locked by caller.
*/
int
xfs_trans_reserve_quota_bydquots(
xfs_trans_t *tp,
xfs_mount_t *mp,
xfs_dquot_t *udqp,
xfs_dquot_t *gdqp,
long nblks,
long ninos,
uint flags)
{
int resvd = 0, error;
if (!XFS_IS_QUOTA_RUNNING(mp) || !XFS_IS_QUOTA_ON(mp))
return 0;
if (tp && tp->t_dqinfo == NULL)
xfs_trans_alloc_dqinfo(tp);
ASSERT(flags & XFS_QMOPT_RESBLK_MASK);
if (udqp) {
error = xfs_trans_dqresv(tp, mp, udqp, nblks, ninos,
(flags & ~XFS_QMOPT_ENOSPC));
if (error)
return error;
resvd = 1;
}
if (gdqp) {
error = xfs_trans_dqresv(tp, mp, gdqp, nblks, ninos, flags);
if (error) {
/*
* can't do it, so backout previous reservation
*/
if (resvd) {
flags |= XFS_QMOPT_FORCE_RES;
xfs_trans_dqresv(tp, mp, udqp,
-nblks, -ninos, flags);
}
return error;
}
}
/*
* Didn't change anything critical, so, no need to log
*/
return 0;
}
/*
* Lock the dquot and change the reservation if we can.
* This doesn't change the actual usage, just the reservation.
* The inode sent in is locked.
*/
int
xfs_trans_reserve_quota_nblks(
struct xfs_trans *tp,
struct xfs_inode *ip,
long nblks,
long ninos,
uint flags)
{
struct xfs_mount *mp = ip->i_mount;
if (!XFS_IS_QUOTA_RUNNING(mp) || !XFS_IS_QUOTA_ON(mp))
return 0;
if (XFS_IS_PQUOTA_ON(mp))
flags |= XFS_QMOPT_ENOSPC;
ASSERT(ip->i_ino != mp->m_sb.sb_uquotino);
ASSERT(ip->i_ino != mp->m_sb.sb_gquotino);
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
ASSERT((flags & ~(XFS_QMOPT_FORCE_RES | XFS_QMOPT_ENOSPC)) ==
XFS_TRANS_DQ_RES_RTBLKS ||
(flags & ~(XFS_QMOPT_FORCE_RES | XFS_QMOPT_ENOSPC)) ==
XFS_TRANS_DQ_RES_BLKS);
/*
* Reserve nblks against these dquots, with trans as the mediator.
*/
return xfs_trans_reserve_quota_bydquots(tp, mp,
ip->i_udquot, ip->i_gdquot,
nblks, ninos, flags);
}
/*
* This routine is called to allocate a quotaoff log item.
*/
xfs_qoff_logitem_t *
xfs_trans_get_qoff_item(
xfs_trans_t *tp,
xfs_qoff_logitem_t *startqoff,
uint flags)
{
xfs_qoff_logitem_t *q;
ASSERT(tp != NULL);
q = xfs_qm_qoff_logitem_init(tp->t_mountp, startqoff, flags);
ASSERT(q != NULL);
/*
* Get a log_item_desc to point at the new item.
*/
(void) xfs_trans_add_item(tp, (xfs_log_item_t*)q);
return (q);
}
/*
* This is called to mark the quotaoff logitem as needing
* to be logged when the transaction is committed. The logitem must
* already be associated with the given transaction.
*/
void
xfs_trans_log_quotaoff_item(
xfs_trans_t *tp,
xfs_qoff_logitem_t *qlp)
{
xfs_log_item_desc_t *lidp;
lidp = xfs_trans_find_item(tp, (xfs_log_item_t *)qlp);
ASSERT(lidp != NULL);
tp->t_flags |= XFS_TRANS_DIRTY;
lidp->lid_flags |= XFS_LID_DIRTY;
}
STATIC void
xfs_trans_alloc_dqinfo(
xfs_trans_t *tp)
{
tp->t_dqinfo = kmem_zone_zalloc(xfs_Gqm->qm_dqtrxzone, KM_SLEEP);
}
void
xfs_trans_free_dqinfo(
xfs_trans_t *tp)
{
if (!tp->t_dqinfo)
return;
kmem_zone_free(xfs_Gqm->qm_dqtrxzone, tp->t_dqinfo);
tp->t_dqinfo = NULL;
}