+/* Grab the dirty and seen grant announcements from the incoming obdo.
+ * We will later calculate the clients new grant and return it.
+ * Caller must hold osfs lock */
+static void filter_grant_incoming(struct obd_export *exp, struct obdo *oa)
+{
+ struct filter_export_data *fed;
+ struct obd_device *obd = exp->exp_obd;
+ ENTRY;
+
+ if ((oa->o_valid & (OBD_MD_FLBLOCKS|OBD_MD_FLGRANT)) !=
+ (OBD_MD_FLBLOCKS|OBD_MD_FLGRANT)) {
+ oa->o_valid &= ~OBD_MD_FLGRANT;
+ EXIT;
+ return;
+ }
+
+ fed = &exp->exp_filter_data;
+
+ /* Add some margin, since there is a small race if other RPCs arrive
+ * out-or-order and have already consumed some grant. We want to
+ * leave this here in case there is a large error in accounting. */
+ CDEBUG(oa->o_grant > fed->fed_grant + FILTER_GRANT_CHUNK ?
+ D_ERROR : D_CACHE,
+ "%s: cli %s reports granted: "LPU64" dropped: %u, local: %lu\n",
+ obd->obd_name, exp->exp_client_uuid.uuid, oa->o_grant,
+ oa->o_dropped, fed->fed_grant);
+
+ /* Update our accounting now so that statfs takes it into account.
+ * Note that fed_dirty is only approximate and can become incorrect
+ * if RPCs arrive out-of-order. No important calculations depend
+ * on fed_dirty however. */
+ obd->u.filter.fo_tot_dirty += oa->o_dirty - fed->fed_dirty;
+ if (fed->fed_grant < oa->o_dropped) {
+ CERROR("%s: cli %s reports %u dropped > fed_grant %lu\n",
+ obd->obd_name, exp->exp_client_uuid.uuid,
+ oa->o_dropped, fed->fed_grant);
+ oa->o_dropped = 0;
+ }
+ if (obd->u.filter.fo_tot_granted < oa->o_dropped) {
+ CERROR("%s: cli %s reports %u dropped > tot_granted "LPU64"\n",
+ obd->obd_name, exp->exp_client_uuid.uuid,
+ oa->o_dropped, obd->u.filter.fo_tot_granted);
+ oa->o_dropped = 0;
+ }
+ obd->u.filter.fo_tot_granted -= oa->o_dropped;
+ fed->fed_grant -= oa->o_dropped;
+ fed->fed_dirty = oa->o_dirty;
+ EXIT;
+}
+
+#define GRANT_FOR_LLOG 16
+
+/* Figure out how much space is available between what we've granted
+ * and what remains in the filesystem. Compensate for ext3 indirect
+ * block overhead when computing how much free space is left ungranted.
+ *
+ * Caller must hold obd_osfs_lock. */
+obd_size filter_grant_space_left(struct obd_export *exp)
+{
+ struct obd_device *obd = exp->exp_obd;
+ int blockbits = obd->u.filter.fo_sb->s_blocksize_bits;
+ obd_size tot_granted = obd->u.filter.fo_tot_granted, avail, left = 0;
+ int rc, statfs_done = 0;
+
+ if (time_before(obd->obd_osfs_age, jiffies - HZ)) {
+restat:
+ rc = fsfilt_statfs(obd, obd->u.filter.fo_sb, jiffies + 1);
+ if (rc) /* N.B. statfs can't really fail */
+ RETURN(0);
+ statfs_done = 1;
+ }
+
+ avail = obd->obd_osfs.os_bavail;
+ left = avail - (avail >> (blockbits - 3)); /* (d)indirect */
+ if (left > GRANT_FOR_LLOG) {
+ left = (left - GRANT_FOR_LLOG) << blockbits;
+ } else {
+ left = 0 /* << blockbits */;
+ }
+
+ if (!statfs_done && left < 32 * FILTER_GRANT_CHUNK + tot_granted) {
+ CDEBUG(D_CACHE, "fs has no space left and statfs too old\n");
+ goto restat;
+ }
+
+ if (left >= tot_granted) {
+ left -= tot_granted;
+ } else {
+ static unsigned long next;
+ if (left < tot_granted - obd->u.filter.fo_tot_pending &&
+ time_after(jiffies, next)) {
+ spin_unlock(&obd->obd_osfs_lock);
+ CERROR("%s: cli %s granted "LPU64" more than available "
+ LPU64" and pending "LPU64"\n", obd->obd_name,
+ exp->exp_client_uuid.uuid, tot_granted, left,
+ obd->u.filter.fo_tot_pending);
+ if (next == 0)
+ portals_debug_dumplog();
+ next = jiffies + 20 * HZ;
+ spin_lock(&obd->obd_osfs_lock);
+ }
+ left = 0;
+ }
+
+ CDEBUG(D_CACHE, "%s: cli %s free: "LPU64" avail: "LPU64" grant "LPU64
+ " left: "LPU64" pending: "LPU64"\n", obd->obd_name,
+ exp->exp_client_uuid.uuid, obd->obd_osfs.os_bfree << blockbits,
+ avail << blockbits, tot_granted, left,
+ obd->u.filter.fo_tot_pending);
+
+ return left;
+}
+
+/* Calculate how much grant space to allocate to this client, based on how
+ * much space is currently free and how much of that is already granted.
+ *
+ * Caller must hold obd_osfs_lock. */
+long filter_grant(struct obd_export *exp, obd_size current_grant,
+ obd_size want, obd_size fs_space_left)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct filter_export_data *fed = &exp->exp_filter_data;
+ int blockbits = obd->u.filter.fo_sb->s_blocksize_bits;
+ __u64 grant = 0;
+
+ /* Grant some fraction of the client's requested grant space so that
+ * they are not always waiting for write credits (not all of it to
+ * avoid overgranting in face of multiple RPCs in flight). This
+ * essentially will be able to control the OSC_MAX_RIF for a client.
+ *
+ * If we do have a large disparity and multiple RPCs in flight we
+ * might grant "too much" but that's OK because it means we are
+ * dirtying a lot on the client and will likely use it up quickly. */
+ if (current_grant < want) {
+ grant = min((want >> blockbits) / 2,
+ (fs_space_left >> blockbits) / 8);
+ grant <<= blockbits;
+
+ if (grant) {
+ if (grant > FILTER_GRANT_CHUNK)
+ grant = FILTER_GRANT_CHUNK;
+
+ obd->u.filter.fo_tot_granted += grant;
+ fed->fed_grant += grant;
+ }
+ }
+
+ CDEBUG(D_CACHE,"%s: cli %s wants: "LPU64" granting: "LPU64"\n",
+ obd->obd_name, exp->exp_client_uuid.uuid, want, grant);
+ CDEBUG(D_CACHE,
+ "%s: cli %s tot cached:"LPU64" granted:"LPU64
+ " num_exports: %d\n", obd->obd_name, exp->exp_client_uuid.uuid,
+ obd->u.filter.fo_tot_dirty,
+ obd->u.filter.fo_tot_granted, obd->obd_num_exports);
+
+ return grant;
+}
+