GFS2: merge window
Here is a list of patches we've accumulated for GFS2 for the current upstream merge window. We have a good mixture this time. Here are some of the features: 1. Fix a problem with RO mounts writing to the journal. 2. Further improvements to quotas on GFS2. 3. Added support for rename2 and RENAME_EXCHANGE on GFS2. 4. Increase performance by making glock lru_list less of a bottleneck. 5. Increase performance by avoiding unnecessary buffer_head releases. 6. Increase performance by using average glock round trip time from all CPUs. 7. Fixes for some compiler warnings and minor white space issues. 8. Other misc. bug fixes -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEbBAABAgAGBQJVjFEXAAoJENeLYdPf93o7zqoH926XV0oddQCsGCYg6gq7OL+c /4q2y9x31hkv5XbPTNcahqR6UsK8JcbcdZD+XAqTftL4Q789FDAdWbYS++45qz8D YuUFgZd2bc75Ge2qqacgEdv85YRLtws1fRnI6DUpjfN1qJ9kJXX+gk+BSve6rh6V Qyv8a4DRIw1En5fwt0R6yIg0LI/ywPhYeVlxo6WUoK8fiL/i3eNd57Jgv5YQ6ly+ ZyqH8w1m0kE4IkIiTFgmIpvepiWLBCA3mPOfHfE3QxbDKpXe4uFsMdnxghmP5bFN 3H9syJQNFqjs+ooKma/fE2VmpxQ5/5lAs0/ms+ECW3GvBseTll8Iln7y4NwgAQ== =ITwD -----END PGP SIGNATURE----- Merge tag 'gfs2-merge-window' of git://git.kernel.org:/pub/scm/linux/kernel/git/gfs2/linux-gfs2 Pull GFS2 updates from Bob Peterson: "Here are the patches we've accumulated for GFS2 for the current upstream merge window. We have a good mixture this time. Here are some of the features: - Fix a problem with RO mounts writing to the journal. - Further improvements to quotas on GFS2. - Added support for rename2 and RENAME_EXCHANGE on GFS2. - Increase performance by making glock lru_list less of a bottleneck. - Increase performance by avoiding unnecessary buffer_head releases. - Increase performance by using average glock round trip time from all CPUs. - Fixes for some compiler warnings and minor white space issues. - Other misc bug fixes" * tag 'gfs2-merge-window' of git://git.kernel.org:/pub/scm/linux/kernel/git/gfs2/linux-gfs2: GFS2: Don't brelse rgrp buffer_heads every allocation GFS2: Don't add all glocks to the lru gfs2: Don't support fallocate on jdata files gfs2: s64 cast for negative quota value gfs2: limit quota log messages gfs2: fix quota updates on block boundaries gfs2: fix shadow warning in gfs2_rbm_find() gfs2: kerneldoc warning fixes gfs2: convert simple_str to kstr GFS2: make sure S_NOSEC flag isn't overwritten GFS2: add support for rename2 and RENAME_EXCHANGE gfs2: handle NULL rgd in set_rgrp_preferences GFS2: inode.c: indent with TABs, not spaces GFS2: mark the journal idle to fix ro mounts GFS2: Average in only non-zero round-trip times for congestion stats GFS2: Use average srttb value in congestion calculations
This commit is contained in:
commit
546fac6073
11 changed files with 435 additions and 145 deletions
|
@ -171,6 +171,7 @@ static int __gfs2_jdata_writepage(struct page *page, struct writeback_control *w
|
|||
/**
|
||||
* gfs2_jdata_writepage - Write complete page
|
||||
* @page: Page to write
|
||||
* @wbc: The writeback control
|
||||
*
|
||||
* Returns: errno
|
||||
*
|
||||
|
@ -221,9 +222,10 @@ static int gfs2_writepages(struct address_space *mapping,
|
|||
* gfs2_write_jdata_pagevec - Write back a pagevec's worth of pages
|
||||
* @mapping: The mapping
|
||||
* @wbc: The writeback control
|
||||
* @writepage: The writepage function to call for each page
|
||||
* @pvec: The vector of pages
|
||||
* @nr_pages: The number of pages to write
|
||||
* @end: End position
|
||||
* @done_index: Page index
|
||||
*
|
||||
* Returns: non-zero if loop should terminate, zero otherwise
|
||||
*/
|
||||
|
@ -333,8 +335,6 @@ static int gfs2_write_jdata_pagevec(struct address_space *mapping,
|
|||
* gfs2_write_cache_jdata - Like write_cache_pages but different
|
||||
* @mapping: The mapping to write
|
||||
* @wbc: The writeback control
|
||||
* @writepage: The writepage function to call
|
||||
* @data: The data to pass to writepage
|
||||
*
|
||||
* The reason that we use our own function here is that we need to
|
||||
* start transactions before we grab page locks. This allows us
|
||||
|
@ -588,6 +588,10 @@ int gfs2_internal_read(struct gfs2_inode *ip, char *buf, loff_t *pos,
|
|||
|
||||
/**
|
||||
* gfs2_readpages - Read a bunch of pages at once
|
||||
* @file: The file to read from
|
||||
* @mapping: Address space info
|
||||
* @pages: List of pages to read
|
||||
* @nr_pages: Number of pages to read
|
||||
*
|
||||
* Some notes:
|
||||
* 1. This is only for readahead, so we can simply ignore any things
|
||||
|
@ -853,7 +857,7 @@ static int gfs2_stuffed_write_end(struct inode *inode, struct buffer_head *dibh,
|
|||
* @mapping: The address space to write to
|
||||
* @pos: The file position
|
||||
* @len: The length of the data
|
||||
* @copied:
|
||||
* @copied: How much was actually copied by the VFS
|
||||
* @page: The page that has been written
|
||||
* @fsdata: The fsdata (unused in GFS2)
|
||||
*
|
||||
|
|
|
@ -180,7 +180,7 @@ void gfs2_set_inode_flags(struct inode *inode)
|
|||
|
||||
flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|S_NOSEC);
|
||||
if ((ip->i_eattr == 0) && !is_sxid(inode->i_mode))
|
||||
inode->i_flags |= S_NOSEC;
|
||||
flags |= S_NOSEC;
|
||||
if (ip->i_diskflags & GFS2_DIF_IMMUTABLE)
|
||||
flags |= S_IMMUTABLE;
|
||||
if (ip->i_diskflags & GFS2_DIF_APPENDONLY)
|
||||
|
@ -917,7 +917,7 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t le
|
|||
struct gfs2_holder gh;
|
||||
int ret;
|
||||
|
||||
if (mode & ~FALLOC_FL_KEEP_SIZE)
|
||||
if ((mode & ~FALLOC_FL_KEEP_SIZE) || gfs2_is_jdata(ip))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
|
|
@ -1076,7 +1076,8 @@ void gfs2_glock_dq(struct gfs2_holder *gh)
|
|||
!test_bit(GLF_DEMOTE, &gl->gl_flags))
|
||||
fast_path = 1;
|
||||
}
|
||||
if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl))
|
||||
if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl) &&
|
||||
(glops->go_flags & GLOF_LRU))
|
||||
gfs2_glock_add_to_lru(gl);
|
||||
|
||||
trace_gfs2_glock_queue(gh, 0);
|
||||
|
|
|
@ -144,6 +144,12 @@ static void rgrp_go_sync(struct gfs2_glock *gl)
|
|||
struct gfs2_rgrpd *rgd;
|
||||
int error;
|
||||
|
||||
spin_lock(&gl->gl_spin);
|
||||
rgd = gl->gl_object;
|
||||
if (rgd)
|
||||
gfs2_rgrp_brelse(rgd);
|
||||
spin_unlock(&gl->gl_spin);
|
||||
|
||||
if (!test_and_clear_bit(GLF_DIRTY, &gl->gl_flags))
|
||||
return;
|
||||
GLOCK_BUG_ON(gl, gl->gl_state != LM_ST_EXCLUSIVE);
|
||||
|
@ -175,15 +181,17 @@ static void rgrp_go_inval(struct gfs2_glock *gl, int flags)
|
|||
{
|
||||
struct gfs2_sbd *sdp = gl->gl_sbd;
|
||||
struct address_space *mapping = &sdp->sd_aspace;
|
||||
struct gfs2_rgrpd *rgd = gl->gl_object;
|
||||
|
||||
if (rgd)
|
||||
gfs2_rgrp_brelse(rgd);
|
||||
|
||||
WARN_ON_ONCE(!(flags & DIO_METADATA));
|
||||
gfs2_assert_withdraw(sdp, !atomic_read(&gl->gl_ail_count));
|
||||
truncate_inode_pages_range(mapping, gl->gl_vm.start, gl->gl_vm.end);
|
||||
|
||||
if (gl->gl_object) {
|
||||
struct gfs2_rgrpd *rgd = (struct gfs2_rgrpd *)gl->gl_object;
|
||||
if (rgd)
|
||||
rgd->rd_flags &= ~GFS2_RDF_UPTODATE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -561,7 +569,7 @@ const struct gfs2_glock_operations gfs2_inode_glops = {
|
|||
.go_lock = inode_go_lock,
|
||||
.go_dump = inode_go_dump,
|
||||
.go_type = LM_TYPE_INODE,
|
||||
.go_flags = GLOF_ASPACE,
|
||||
.go_flags = GLOF_ASPACE | GLOF_LRU,
|
||||
};
|
||||
|
||||
const struct gfs2_glock_operations gfs2_rgrp_glops = {
|
||||
|
@ -584,10 +592,12 @@ const struct gfs2_glock_operations gfs2_freeze_glops = {
|
|||
const struct gfs2_glock_operations gfs2_iopen_glops = {
|
||||
.go_type = LM_TYPE_IOPEN,
|
||||
.go_callback = iopen_go_callback,
|
||||
.go_flags = GLOF_LRU,
|
||||
};
|
||||
|
||||
const struct gfs2_glock_operations gfs2_flock_glops = {
|
||||
.go_type = LM_TYPE_FLOCK,
|
||||
.go_flags = GLOF_LRU,
|
||||
};
|
||||
|
||||
const struct gfs2_glock_operations gfs2_nondisk_glops = {
|
||||
|
@ -596,7 +606,7 @@ const struct gfs2_glock_operations gfs2_nondisk_glops = {
|
|||
|
||||
const struct gfs2_glock_operations gfs2_quota_glops = {
|
||||
.go_type = LM_TYPE_QUOTA,
|
||||
.go_flags = GLOF_LVB,
|
||||
.go_flags = GLOF_LVB | GLOF_LRU,
|
||||
};
|
||||
|
||||
const struct gfs2_glock_operations gfs2_journal_glops = {
|
||||
|
|
|
@ -225,6 +225,7 @@ struct gfs2_glock_operations {
|
|||
const unsigned long go_flags;
|
||||
#define GLOF_ASPACE 1
|
||||
#define GLOF_LVB 2
|
||||
#define GLOF_LRU 4
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -432,6 +433,7 @@ enum {
|
|||
QDF_CHANGE = 1,
|
||||
QDF_LOCKED = 2,
|
||||
QDF_REFRESH = 3,
|
||||
QDF_QMSG_QUIET = 4,
|
||||
};
|
||||
|
||||
struct gfs2_quota_data {
|
||||
|
|
211
fs/gfs2/inode.c
211
fs/gfs2/inode.c
|
@ -1227,8 +1227,8 @@ static int gfs2_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|||
*/
|
||||
|
||||
static int gfs2_atomic_open(struct inode *dir, struct dentry *dentry,
|
||||
struct file *file, unsigned flags,
|
||||
umode_t mode, int *opened)
|
||||
struct file *file, unsigned flags,
|
||||
umode_t mode, int *opened)
|
||||
{
|
||||
struct dentry *d;
|
||||
bool excl = !!(flags & O_EXCL);
|
||||
|
@ -1306,6 +1306,35 @@ static int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to)
|
|||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* update_moved_ino - Update an inode that's being moved
|
||||
* @ip: The inode being moved
|
||||
* @ndip: The parent directory of the new filename
|
||||
* @dir_rename: True of ip is a directory
|
||||
*
|
||||
* Returns: errno
|
||||
*/
|
||||
|
||||
static int update_moved_ino(struct gfs2_inode *ip, struct gfs2_inode *ndip,
|
||||
int dir_rename)
|
||||
{
|
||||
int error;
|
||||
struct buffer_head *dibh;
|
||||
|
||||
if (dir_rename)
|
||||
return gfs2_dir_mvino(ip, &gfs2_qdotdot, ndip, DT_DIR);
|
||||
|
||||
error = gfs2_meta_inode_buffer(ip, &dibh);
|
||||
if (error)
|
||||
return error;
|
||||
ip->i_inode.i_ctime = CURRENT_TIME;
|
||||
gfs2_trans_add_meta(ip->i_gl, dibh);
|
||||
gfs2_dinode_out(ip, dibh->b_data);
|
||||
brelse(dibh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gfs2_rename - Rename a file
|
||||
* @odir: Parent directory of old file name
|
||||
|
@ -1354,7 +1383,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
|
|||
|
||||
if (S_ISDIR(ip->i_inode.i_mode)) {
|
||||
dir_rename = 1;
|
||||
/* don't move a dirctory into it's subdir */
|
||||
/* don't move a directory into its subdir */
|
||||
error = gfs2_ok_to_move(ip, ndip);
|
||||
if (error)
|
||||
goto out_gunlock_r;
|
||||
|
@ -1494,20 +1523,9 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
|
|||
if (nip)
|
||||
error = gfs2_unlink_inode(ndip, ndentry);
|
||||
|
||||
if (dir_rename) {
|
||||
error = gfs2_dir_mvino(ip, &gfs2_qdotdot, ndip, DT_DIR);
|
||||
if (error)
|
||||
goto out_end_trans;
|
||||
} else {
|
||||
struct buffer_head *dibh;
|
||||
error = gfs2_meta_inode_buffer(ip, &dibh);
|
||||
if (error)
|
||||
goto out_end_trans;
|
||||
ip->i_inode.i_ctime = CURRENT_TIME;
|
||||
gfs2_trans_add_meta(ip->i_gl, dibh);
|
||||
gfs2_dinode_out(ip, dibh->b_data);
|
||||
brelse(dibh);
|
||||
}
|
||||
error = update_moved_ino(ip, ndip, dir_rename);
|
||||
if (error)
|
||||
goto out_end_trans;
|
||||
|
||||
error = gfs2_dir_del(odip, odentry);
|
||||
if (error)
|
||||
|
@ -1538,6 +1556,161 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
|
|||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_exchange - exchange two files
|
||||
* @odir: Parent directory of old file name
|
||||
* @odentry: The old dentry of the file
|
||||
* @ndir: Parent directory of new file name
|
||||
* @ndentry: The new dentry of the file
|
||||
* @flags: The rename flags
|
||||
*
|
||||
* Returns: errno
|
||||
*/
|
||||
|
||||
static int gfs2_exchange(struct inode *odir, struct dentry *odentry,
|
||||
struct inode *ndir, struct dentry *ndentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct gfs2_inode *odip = GFS2_I(odir);
|
||||
struct gfs2_inode *ndip = GFS2_I(ndir);
|
||||
struct gfs2_inode *oip = GFS2_I(odentry->d_inode);
|
||||
struct gfs2_inode *nip = GFS2_I(ndentry->d_inode);
|
||||
struct gfs2_sbd *sdp = GFS2_SB(odir);
|
||||
struct gfs2_holder ghs[5], r_gh = { .gh_gl = NULL, };
|
||||
unsigned int num_gh;
|
||||
unsigned int x;
|
||||
umode_t old_mode = oip->i_inode.i_mode;
|
||||
umode_t new_mode = nip->i_inode.i_mode;
|
||||
int error;
|
||||
|
||||
error = gfs2_rindex_update(sdp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (odip != ndip) {
|
||||
error = gfs2_glock_nq_init(sdp->sd_rename_gl, LM_ST_EXCLUSIVE,
|
||||
0, &r_gh);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
if (S_ISDIR(old_mode)) {
|
||||
/* don't move a directory into its subdir */
|
||||
error = gfs2_ok_to_move(oip, ndip);
|
||||
if (error)
|
||||
goto out_gunlock_r;
|
||||
}
|
||||
|
||||
if (S_ISDIR(new_mode)) {
|
||||
/* don't move a directory into its subdir */
|
||||
error = gfs2_ok_to_move(nip, odip);
|
||||
if (error)
|
||||
goto out_gunlock_r;
|
||||
}
|
||||
}
|
||||
|
||||
num_gh = 1;
|
||||
gfs2_holder_init(odip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
|
||||
if (odip != ndip) {
|
||||
gfs2_holder_init(ndip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh);
|
||||
num_gh++;
|
||||
}
|
||||
gfs2_holder_init(oip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh);
|
||||
num_gh++;
|
||||
|
||||
gfs2_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh);
|
||||
num_gh++;
|
||||
|
||||
for (x = 0; x < num_gh; x++) {
|
||||
error = gfs2_glock_nq(ghs + x);
|
||||
if (error)
|
||||
goto out_gunlock;
|
||||
}
|
||||
|
||||
error = -ENOENT;
|
||||
if (oip->i_inode.i_nlink == 0 || nip->i_inode.i_nlink == 0)
|
||||
goto out_gunlock;
|
||||
|
||||
error = gfs2_unlink_ok(odip, &odentry->d_name, oip);
|
||||
if (error)
|
||||
goto out_gunlock;
|
||||
error = gfs2_unlink_ok(ndip, &ndentry->d_name, nip);
|
||||
if (error)
|
||||
goto out_gunlock;
|
||||
|
||||
if (S_ISDIR(old_mode)) {
|
||||
error = gfs2_permission(odentry->d_inode, MAY_WRITE);
|
||||
if (error)
|
||||
goto out_gunlock;
|
||||
}
|
||||
if (S_ISDIR(new_mode)) {
|
||||
error = gfs2_permission(ndentry->d_inode, MAY_WRITE);
|
||||
if (error)
|
||||
goto out_gunlock;
|
||||
}
|
||||
error = gfs2_trans_begin(sdp, 4 * RES_DINODE + 4 * RES_LEAF, 0);
|
||||
if (error)
|
||||
goto out_gunlock;
|
||||
|
||||
error = update_moved_ino(oip, ndip, S_ISDIR(old_mode));
|
||||
if (error)
|
||||
goto out_end_trans;
|
||||
|
||||
error = update_moved_ino(nip, odip, S_ISDIR(new_mode));
|
||||
if (error)
|
||||
goto out_end_trans;
|
||||
|
||||
error = gfs2_dir_mvino(ndip, &ndentry->d_name, oip,
|
||||
IF2DT(old_mode));
|
||||
if (error)
|
||||
goto out_end_trans;
|
||||
|
||||
error = gfs2_dir_mvino(odip, &odentry->d_name, nip,
|
||||
IF2DT(new_mode));
|
||||
if (error)
|
||||
goto out_end_trans;
|
||||
|
||||
if (odip != ndip) {
|
||||
if (S_ISDIR(new_mode) && !S_ISDIR(old_mode)) {
|
||||
inc_nlink(&odip->i_inode);
|
||||
drop_nlink(&ndip->i_inode);
|
||||
} else if (S_ISDIR(old_mode) && !S_ISDIR(new_mode)) {
|
||||
inc_nlink(&ndip->i_inode);
|
||||
drop_nlink(&odip->i_inode);
|
||||
}
|
||||
}
|
||||
mark_inode_dirty(&ndip->i_inode);
|
||||
if (odip != ndip)
|
||||
mark_inode_dirty(&odip->i_inode);
|
||||
|
||||
out_end_trans:
|
||||
gfs2_trans_end(sdp);
|
||||
out_gunlock:
|
||||
while (x--) {
|
||||
gfs2_glock_dq(ghs + x);
|
||||
gfs2_holder_uninit(ghs + x);
|
||||
}
|
||||
out_gunlock_r:
|
||||
if (r_gh.gh_gl)
|
||||
gfs2_glock_dq_uninit(&r_gh);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int gfs2_rename2(struct inode *odir, struct dentry *odentry,
|
||||
struct inode *ndir, struct dentry *ndentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
flags &= ~RENAME_NOREPLACE;
|
||||
|
||||
if (flags & ~RENAME_EXCHANGE)
|
||||
return -EINVAL;
|
||||
|
||||
if (flags & RENAME_EXCHANGE)
|
||||
return gfs2_exchange(odir, odentry, ndir, ndentry, flags);
|
||||
|
||||
return gfs2_rename(odir, odentry, ndir, ndentry);
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_follow_link - Follow a symbolic link
|
||||
* @dentry: The dentry of the link
|
||||
|
@ -1716,7 +1889,7 @@ static int setattr_chown(struct inode *inode, struct iattr *attr)
|
|||
|
||||
if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) ||
|
||||
!gid_eq(ogid, NO_GID_QUOTA_CHANGE)) {
|
||||
gfs2_quota_change(ip, -ap.target, ouid, ogid);
|
||||
gfs2_quota_change(ip, -(s64)ap.target, ouid, ogid);
|
||||
gfs2_quota_change(ip, ap.target, nuid, ngid);
|
||||
}
|
||||
|
||||
|
@ -1943,7 +2116,7 @@ const struct inode_operations gfs2_dir_iops = {
|
|||
.mkdir = gfs2_mkdir,
|
||||
.rmdir = gfs2_unlink,
|
||||
.mknod = gfs2_mknod,
|
||||
.rename = gfs2_rename,
|
||||
.rename2 = gfs2_rename2,
|
||||
.permission = gfs2_permission,
|
||||
.setattr = gfs2_setattr,
|
||||
.getattr = gfs2_getattr,
|
||||
|
|
|
@ -756,6 +756,7 @@ static int init_journal(struct gfs2_sbd *sdp, int undo)
|
|||
}
|
||||
}
|
||||
|
||||
sdp->sd_log_idle = 1;
|
||||
set_bit(SDF_JOURNAL_CHECKED, &sdp->sd_flags);
|
||||
gfs2_glock_dq_uninit(&ji_gh);
|
||||
jindex = 0;
|
||||
|
|
212
fs/gfs2/quota.c
212
fs/gfs2/quota.c
|
@ -649,9 +649,117 @@ static void do_qc(struct gfs2_quota_data *qd, s64 change)
|
|||
slot_hold(qd);
|
||||
}
|
||||
|
||||
if (change < 0) /* Reset quiet flag if we freed some blocks */
|
||||
clear_bit(QDF_QMSG_QUIET, &qd->qd_flags);
|
||||
mutex_unlock(&sdp->sd_quota_mutex);
|
||||
}
|
||||
|
||||
static int gfs2_write_buf_to_page(struct gfs2_inode *ip, unsigned long index,
|
||||
unsigned off, void *buf, unsigned bytes)
|
||||
{
|
||||
struct inode *inode = &ip->i_inode;
|
||||
struct gfs2_sbd *sdp = GFS2_SB(inode);
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
struct page *page;
|
||||
struct buffer_head *bh;
|
||||
void *kaddr;
|
||||
u64 blk;
|
||||
unsigned bsize = sdp->sd_sb.sb_bsize, bnum = 0, boff = 0;
|
||||
unsigned to_write = bytes, pg_off = off;
|
||||
int done = 0;
|
||||
|
||||
blk = index << (PAGE_CACHE_SHIFT - sdp->sd_sb.sb_bsize_shift);
|
||||
boff = off % bsize;
|
||||
|
||||
page = find_or_create_page(mapping, index, GFP_NOFS);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
if (!page_has_buffers(page))
|
||||
create_empty_buffers(page, bsize, 0);
|
||||
|
||||
bh = page_buffers(page);
|
||||
while (!done) {
|
||||
/* Find the beginning block within the page */
|
||||
if (pg_off >= ((bnum * bsize) + bsize)) {
|
||||
bh = bh->b_this_page;
|
||||
bnum++;
|
||||
blk++;
|
||||
continue;
|
||||
}
|
||||
if (!buffer_mapped(bh)) {
|
||||
gfs2_block_map(inode, blk, bh, 1);
|
||||
if (!buffer_mapped(bh))
|
||||
goto unlock_out;
|
||||
/* If it's a newly allocated disk block, zero it */
|
||||
if (buffer_new(bh))
|
||||
zero_user(page, bnum * bsize, bh->b_size);
|
||||
}
|
||||
if (PageUptodate(page))
|
||||
set_buffer_uptodate(bh);
|
||||
if (!buffer_uptodate(bh)) {
|
||||
ll_rw_block(READ | REQ_META, 1, &bh);
|
||||
wait_on_buffer(bh);
|
||||
if (!buffer_uptodate(bh))
|
||||
goto unlock_out;
|
||||
}
|
||||
gfs2_trans_add_data(ip->i_gl, bh);
|
||||
|
||||
/* If we need to write to the next block as well */
|
||||
if (to_write > (bsize - boff)) {
|
||||
pg_off += (bsize - boff);
|
||||
to_write -= (bsize - boff);
|
||||
boff = pg_off % bsize;
|
||||
continue;
|
||||
}
|
||||
done = 1;
|
||||
}
|
||||
|
||||
/* Write to the page, now that we have setup the buffer(s) */
|
||||
kaddr = kmap_atomic(page);
|
||||
memcpy(kaddr + off, buf, bytes);
|
||||
flush_dcache_page(page);
|
||||
kunmap_atomic(kaddr);
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
|
||||
return 0;
|
||||
|
||||
unlock_out:
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int gfs2_write_disk_quota(struct gfs2_inode *ip, struct gfs2_quota *qp,
|
||||
loff_t loc)
|
||||
{
|
||||
unsigned long pg_beg;
|
||||
unsigned pg_off, nbytes, overflow = 0;
|
||||
int pg_oflow = 0, error;
|
||||
void *ptr;
|
||||
|
||||
nbytes = sizeof(struct gfs2_quota);
|
||||
|
||||
pg_beg = loc >> PAGE_CACHE_SHIFT;
|
||||
pg_off = loc % PAGE_CACHE_SIZE;
|
||||
|
||||
/* If the quota straddles a page boundary, split the write in two */
|
||||
if ((pg_off + nbytes) > PAGE_CACHE_SIZE) {
|
||||
pg_oflow = 1;
|
||||
overflow = (pg_off + nbytes) - PAGE_CACHE_SIZE;
|
||||
}
|
||||
|
||||
ptr = qp;
|
||||
error = gfs2_write_buf_to_page(ip, pg_beg, pg_off, ptr,
|
||||
nbytes - overflow);
|
||||
/* If there's an overflow, write the remaining bytes to the next page */
|
||||
if (!error && pg_oflow)
|
||||
error = gfs2_write_buf_to_page(ip, pg_beg + 1, 0,
|
||||
ptr + nbytes - overflow,
|
||||
overflow);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_adjust_quota - adjust record of current block usage
|
||||
* @ip: The quota inode
|
||||
|
@ -672,15 +780,8 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc,
|
|||
{
|
||||
struct inode *inode = &ip->i_inode;
|
||||
struct gfs2_sbd *sdp = GFS2_SB(inode);
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
unsigned long index = loc >> PAGE_CACHE_SHIFT;
|
||||
unsigned offset = loc & (PAGE_CACHE_SIZE - 1);
|
||||
unsigned blocksize, iblock, pos;
|
||||
struct buffer_head *bh;
|
||||
struct page *page;
|
||||
void *kaddr, *ptr;
|
||||
struct gfs2_quota q;
|
||||
int err, nbytes;
|
||||
int err;
|
||||
u64 size;
|
||||
|
||||
if (gfs2_is_stuffed(ip)) {
|
||||
|
@ -694,8 +795,11 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
loc -= sizeof(q); /* gfs2_internal_read would've advanced the loc ptr */
|
||||
err = -EIO;
|
||||
be64_add_cpu(&q.qu_value, change);
|
||||
if (((s64)be64_to_cpu(q.qu_value)) < 0)
|
||||
q.qu_value = 0; /* Never go negative on quota usage */
|
||||
qd->qd_qb.qb_value = q.qu_value;
|
||||
if (fdq) {
|
||||
if (fdq->d_fieldmask & QC_SPC_SOFT) {
|
||||
|
@ -712,79 +816,16 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc,
|
|||
}
|
||||
}
|
||||
|
||||
/* Write the quota into the quota file on disk */
|
||||
ptr = &q;
|
||||
nbytes = sizeof(struct gfs2_quota);
|
||||
get_a_page:
|
||||
page = find_or_create_page(mapping, index, GFP_NOFS);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
|
||||
blocksize = inode->i_sb->s_blocksize;
|
||||
iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
|
||||
|
||||
if (!page_has_buffers(page))
|
||||
create_empty_buffers(page, blocksize, 0);
|
||||
|
||||
bh = page_buffers(page);
|
||||
pos = blocksize;
|
||||
while (offset >= pos) {
|
||||
bh = bh->b_this_page;
|
||||
iblock++;
|
||||
pos += blocksize;
|
||||
err = gfs2_write_disk_quota(ip, &q, loc);
|
||||
if (!err) {
|
||||
size = loc + sizeof(struct gfs2_quota);
|
||||
if (size > inode->i_size)
|
||||
i_size_write(inode, size);
|
||||
inode->i_mtime = inode->i_atime = CURRENT_TIME;
|
||||
mark_inode_dirty(inode);
|
||||
set_bit(QDF_REFRESH, &qd->qd_flags);
|
||||
}
|
||||
|
||||
if (!buffer_mapped(bh)) {
|
||||
gfs2_block_map(inode, iblock, bh, 1);
|
||||
if (!buffer_mapped(bh))
|
||||
goto unlock_out;
|
||||
/* If it's a newly allocated disk block for quota, zero it */
|
||||
if (buffer_new(bh))
|
||||
zero_user(page, pos - blocksize, bh->b_size);
|
||||
}
|
||||
|
||||
if (PageUptodate(page))
|
||||
set_buffer_uptodate(bh);
|
||||
|
||||
if (!buffer_uptodate(bh)) {
|
||||
ll_rw_block(READ | REQ_META, 1, &bh);
|
||||
wait_on_buffer(bh);
|
||||
if (!buffer_uptodate(bh))
|
||||
goto unlock_out;
|
||||
}
|
||||
|
||||
gfs2_trans_add_data(ip->i_gl, bh);
|
||||
|
||||
kaddr = kmap_atomic(page);
|
||||
if (offset + sizeof(struct gfs2_quota) > PAGE_CACHE_SIZE)
|
||||
nbytes = PAGE_CACHE_SIZE - offset;
|
||||
memcpy(kaddr + offset, ptr, nbytes);
|
||||
flush_dcache_page(page);
|
||||
kunmap_atomic(kaddr);
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
|
||||
/* If quota straddles page boundary, we need to update the rest of the
|
||||
* quota at the beginning of the next page */
|
||||
if ((offset + sizeof(struct gfs2_quota)) > PAGE_CACHE_SIZE) {
|
||||
ptr = ptr + nbytes;
|
||||
nbytes = sizeof(struct gfs2_quota) - nbytes;
|
||||
offset = 0;
|
||||
index++;
|
||||
goto get_a_page;
|
||||
}
|
||||
|
||||
size = loc + sizeof(struct gfs2_quota);
|
||||
if (size > inode->i_size)
|
||||
i_size_write(inode, size);
|
||||
inode->i_mtime = inode->i_atime = CURRENT_TIME;
|
||||
mark_inode_dirty(inode);
|
||||
set_bit(QDF_REFRESH, &qd->qd_flags);
|
||||
return 0;
|
||||
|
||||
unlock_out:
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -1148,10 +1189,13 @@ int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid,
|
|||
/* If no min_target specified or we don't meet
|
||||
* min_target, return -EDQUOT */
|
||||
if (!ap->min_target || ap->min_target > ap->allowed) {
|
||||
print_message(qd, "exceeded");
|
||||
quota_send_warning(qd->qd_id,
|
||||
sdp->sd_vfs->s_dev,
|
||||
QUOTA_NL_BHARDWARN);
|
||||
if (!test_and_set_bit(QDF_QMSG_QUIET,
|
||||
&qd->qd_flags)) {
|
||||
print_message(qd, "exceeded");
|
||||
quota_send_warning(qd->qd_id,
|
||||
sdp->sd_vfs->s_dev,
|
||||
QUOTA_NL_BHARDWARN);
|
||||
}
|
||||
error = -EDQUOT;
|
||||
break;
|
||||
}
|
||||
|
@ -1648,6 +1692,8 @@ static int gfs2_set_dqblk(struct super_block *sb, struct kqid qid,
|
|||
|
||||
/* Apply changes */
|
||||
error = gfs2_adjust_quota(ip, offset, 0, qd, fdq);
|
||||
if (!error)
|
||||
clear_bit(QDF_QMSG_QUIET, &qd->qd_flags);
|
||||
|
||||
gfs2_trans_end(sdp);
|
||||
out_release:
|
||||
|
|
|
@ -978,10 +978,10 @@ static void set_rgrp_preferences(struct gfs2_sbd *sdp)
|
|||
rgd->rd_flags |= GFS2_RDF_PREFERRED;
|
||||
for (i = 0; i < sdp->sd_journals; i++) {
|
||||
rgd = gfs2_rgrpd_get_next(rgd);
|
||||
if (rgd == first)
|
||||
if (!rgd || rgd == first)
|
||||
break;
|
||||
}
|
||||
} while (rgd != first);
|
||||
} while (rgd && rgd != first);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1244,14 +1244,13 @@ int gfs2_rgrp_go_lock(struct gfs2_holder *gh)
|
|||
}
|
||||
|
||||
/**
|
||||
* gfs2_rgrp_go_unlock - Release RG bitmaps read in with gfs2_rgrp_bh_get()
|
||||
* @gh: The glock holder for the resource group
|
||||
* gfs2_rgrp_brelse - Release RG bitmaps read in with gfs2_rgrp_bh_get()
|
||||
* @rgd: The resource group
|
||||
*
|
||||
*/
|
||||
|
||||
void gfs2_rgrp_go_unlock(struct gfs2_holder *gh)
|
||||
void gfs2_rgrp_brelse(struct gfs2_rgrpd *rgd)
|
||||
{
|
||||
struct gfs2_rgrpd *rgd = gh->gh_gl->gl_object;
|
||||
int x, length = rgd->rd_length;
|
||||
|
||||
for (x = 0; x < length; x++) {
|
||||
|
@ -1264,6 +1263,22 @@ void gfs2_rgrp_go_unlock(struct gfs2_holder *gh)
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_rgrp_go_unlock - Unlock a rgrp glock
|
||||
* @gh: The glock holder for the resource group
|
||||
*
|
||||
*/
|
||||
|
||||
void gfs2_rgrp_go_unlock(struct gfs2_holder *gh)
|
||||
{
|
||||
struct gfs2_rgrpd *rgd = gh->gh_gl->gl_object;
|
||||
int demote_requested = test_bit(GLF_DEMOTE, &gh->gh_gl->gl_flags) |
|
||||
test_bit(GLF_PENDING_DEMOTE, &gh->gh_gl->gl_flags);
|
||||
|
||||
if (rgd && demote_requested)
|
||||
gfs2_rgrp_brelse(rgd);
|
||||
}
|
||||
|
||||
int gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 offset,
|
||||
struct buffer_head *bh,
|
||||
const struct gfs2_bitmap *bi, unsigned minlen, u64 *ptrimmed)
|
||||
|
@ -1711,10 +1726,8 @@ static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, u32 *minext,
|
|||
return ret;
|
||||
|
||||
bitmap_full: /* Mark bitmap as full and fall through */
|
||||
if ((state == GFS2_BLKST_FREE) && initial_offset == 0) {
|
||||
struct gfs2_bitmap *bi = rbm_bi(rbm);
|
||||
if ((state == GFS2_BLKST_FREE) && initial_offset == 0)
|
||||
set_bit(GBF_FULL, &bi->bi_flags);
|
||||
}
|
||||
|
||||
next_bitmap: /* Find next bitmap in the rgrp */
|
||||
rbm->offset = 0;
|
||||
|
@ -1850,14 +1863,23 @@ static bool gfs2_rgrp_congested(const struct gfs2_rgrpd *rgd, int loops)
|
|||
const struct gfs2_sbd *sdp = gl->gl_sbd;
|
||||
struct gfs2_lkstats *st;
|
||||
s64 r_dcount, l_dcount;
|
||||
s64 r_srttb, l_srttb;
|
||||
s64 l_srttb, a_srttb = 0;
|
||||
s64 srttb_diff;
|
||||
s64 sqr_diff;
|
||||
s64 var;
|
||||
int cpu, nonzero = 0;
|
||||
|
||||
preempt_disable();
|
||||
for_each_present_cpu(cpu) {
|
||||
st = &per_cpu_ptr(sdp->sd_lkstats, cpu)->lkstats[LM_TYPE_RGRP];
|
||||
if (st->stats[GFS2_LKS_SRTTB]) {
|
||||
a_srttb += st->stats[GFS2_LKS_SRTTB];
|
||||
nonzero++;
|
||||
}
|
||||
}
|
||||
st = &this_cpu_ptr(sdp->sd_lkstats)->lkstats[LM_TYPE_RGRP];
|
||||
r_srttb = st->stats[GFS2_LKS_SRTTB];
|
||||
if (nonzero)
|
||||
do_div(a_srttb, nonzero);
|
||||
r_dcount = st->stats[GFS2_LKS_DCOUNT];
|
||||
var = st->stats[GFS2_LKS_SRTTVARB] +
|
||||
gl->gl_stats.stats[GFS2_LKS_SRTTVARB];
|
||||
|
@ -1866,10 +1888,10 @@ static bool gfs2_rgrp_congested(const struct gfs2_rgrpd *rgd, int loops)
|
|||
l_srttb = gl->gl_stats.stats[GFS2_LKS_SRTTB];
|
||||
l_dcount = gl->gl_stats.stats[GFS2_LKS_DCOUNT];
|
||||
|
||||
if ((l_dcount < 1) || (r_dcount < 1) || (r_srttb == 0))
|
||||
if ((l_dcount < 1) || (r_dcount < 1) || (a_srttb == 0))
|
||||
return false;
|
||||
|
||||
srttb_diff = r_srttb - l_srttb;
|
||||
srttb_diff = a_srttb - l_srttb;
|
||||
sqr_diff = srttb_diff * srttb_diff;
|
||||
|
||||
var *= 2;
|
||||
|
|
|
@ -36,6 +36,7 @@ extern void gfs2_clear_rgrpd(struct gfs2_sbd *sdp);
|
|||
extern int gfs2_rindex_update(struct gfs2_sbd *sdp);
|
||||
extern void gfs2_free_clones(struct gfs2_rgrpd *rgd);
|
||||
extern int gfs2_rgrp_go_lock(struct gfs2_holder *gh);
|
||||
extern void gfs2_rgrp_brelse(struct gfs2_rgrpd *rgd);
|
||||
extern void gfs2_rgrp_go_unlock(struct gfs2_holder *gh);
|
||||
|
||||
extern struct gfs2_alloc *gfs2_alloc_get(struct gfs2_inode *ip);
|
||||
|
|
|
@ -101,8 +101,11 @@ static ssize_t freeze_show(struct gfs2_sbd *sdp, char *buf)
|
|||
|
||||
static ssize_t freeze_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
|
||||
{
|
||||
int error;
|
||||
int n = simple_strtol(buf, NULL, 0);
|
||||
int error, n;
|
||||
|
||||
error = kstrtoint(buf, 0, &n);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
@ -134,10 +137,16 @@ static ssize_t withdraw_show(struct gfs2_sbd *sdp, char *buf)
|
|||
|
||||
static ssize_t withdraw_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
|
||||
{
|
||||
int error, val;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (simple_strtol(buf, NULL, 0) != 1)
|
||||
error = kstrtoint(buf, 0, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (val != 1)
|
||||
return -EINVAL;
|
||||
|
||||
gfs2_lm_withdraw(sdp, "withdrawing from cluster at user's request\n");
|
||||
|
@ -148,10 +157,16 @@ static ssize_t withdraw_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
|
|||
static ssize_t statfs_sync_store(struct gfs2_sbd *sdp, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
int error, val;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (simple_strtol(buf, NULL, 0) != 1)
|
||||
error = kstrtoint(buf, 0, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (val != 1)
|
||||
return -EINVAL;
|
||||
|
||||
gfs2_statfs_sync(sdp->sd_vfs, 0);
|
||||
|
@ -161,10 +176,16 @@ static ssize_t statfs_sync_store(struct gfs2_sbd *sdp, const char *buf,
|
|||
static ssize_t quota_sync_store(struct gfs2_sbd *sdp, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
int error, val;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (simple_strtol(buf, NULL, 0) != 1)
|
||||
error = kstrtoint(buf, 0, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (val != 1)
|
||||
return -EINVAL;
|
||||
|
||||
gfs2_quota_sync(sdp->sd_vfs, 0);
|
||||
|
@ -181,7 +202,9 @@ static ssize_t quota_refresh_user_store(struct gfs2_sbd *sdp, const char *buf,
|
|||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
id = simple_strtoul(buf, NULL, 0);
|
||||
error = kstrtou32(buf, 0, &id);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
qid = make_kqid(current_user_ns(), USRQUOTA, id);
|
||||
if (!qid_valid(qid))
|
||||
|
@ -201,7 +224,9 @@ static ssize_t quota_refresh_group_store(struct gfs2_sbd *sdp, const char *buf,
|
|||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
id = simple_strtoul(buf, NULL, 0);
|
||||
error = kstrtou32(buf, 0, &id);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
qid = make_kqid(current_user_ns(), GRPQUOTA, id);
|
||||
if (!qid_valid(qid))
|
||||
|
@ -324,10 +349,11 @@ static ssize_t block_show(struct gfs2_sbd *sdp, char *buf)
|
|||
static ssize_t block_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
|
||||
{
|
||||
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
|
||||
ssize_t ret = len;
|
||||
int val;
|
||||
int ret, val;
|
||||
|
||||
val = simple_strtol(buf, NULL, 0);
|
||||
ret = kstrtoint(buf, 0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val == 1)
|
||||
set_bit(DFL_BLOCK_LOCKS, &ls->ls_recover_flags);
|
||||
|
@ -336,9 +362,9 @@ static ssize_t block_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
|
|||
smp_mb__after_atomic();
|
||||
gfs2_glock_thaw(sdp);
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t wdack_show(struct gfs2_sbd *sdp, char *buf)
|
||||
|
@ -350,17 +376,18 @@ static ssize_t wdack_show(struct gfs2_sbd *sdp, char *buf)
|
|||
|
||||
static ssize_t wdack_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
|
||||
{
|
||||
ssize_t ret = len;
|
||||
int val;
|
||||
int ret, val;
|
||||
|
||||
val = simple_strtol(buf, NULL, 0);
|
||||
ret = kstrtoint(buf, 0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if ((val == 1) &&
|
||||
!strcmp(sdp->sd_lockstruct.ls_ops->lm_proto_name, "lock_dlm"))
|
||||
complete(&sdp->sd_wdack);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
return -EINVAL;
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t lkfirst_show(struct gfs2_sbd *sdp, char *buf)
|
||||
|
@ -553,11 +580,14 @@ static ssize_t tune_set(struct gfs2_sbd *sdp, unsigned int *field,
|
|||
{
|
||||
struct gfs2_tune *gt = &sdp->sd_tune;
|
||||
unsigned int x;
|
||||
int error;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
x = simple_strtoul(buf, NULL, 0);
|
||||
error = kstrtouint(buf, 0, &x);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (check_zero && !x)
|
||||
return -EINVAL;
|
||||
|
|
Loading…
Reference in a new issue