*/
                if (!gts->ts_force_cch_reload &&
                                        down_read_trylock(>s->ts_mm->mmap_sem)) {
+                       gts->ustats.fmm_tlbdropin++;
                        gru_try_dropin(gts, tfh, NULL);
                        up_read(>s->ts_mm->mmap_sem);
                } else {
        struct gru_mm_struct *gms = gts->ts_gms;
        int ret;
 
+       gts->ustats.upm_tlbdropin++;
        while (1) {
                wait_event(gms->ms_wait_queue,
                           atomic_read(&gms->ms_range_active) == 0);
        return 0;
 }
 
+/*
+ * Fetch GSEG statisticss
+ */
+long gru_get_gseg_statistics(unsigned long arg)
+{
+       struct gru_thread_state *gts;
+       struct gru_get_gseg_statistics_req req;
+
+       if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
+               return -EFAULT;
+
+       gts = gru_find_lock_gts(req.gseg);
+       if (gts) {
+               memcpy(&req.stats, >s->ustats, sizeof(gts->ustats));
+               gru_unlock_gts(gts);
+       } else {
+               memset(&req.stats, 0, sizeof(gts->ustats));
+       }
+
+       if (copy_to_user((void __user *)arg, &req, sizeof(req)))
+               return -EFAULT;
+
+       return 0;
+}
+
 /*
  * Register the current task as the user of the GSEG slice.
  * Needed for TLB fault interrupt targeting.
 
        case GRU_USER_CALL_OS:
                err = gru_handle_user_call_os(arg);
                break;
+       case GRU_GET_GSEG_STATISTICS:
+               err = gru_get_gseg_statistics(arg);
+               break;
        case GRU_KTEST:
                err = gru_ktest(arg);
                break;
 
 /* For dumpping GRU chiplet state */
 #define GRU_DUMP_CHIPLET_STATE         _IOWR(GRU_IOCTL_NUM, 11, void *)
 
+/* For getting gseg statistics */
+#define GRU_GET_GSEG_STATISTICS                _IOWR(GRU_IOCTL_NUM, 12, void *)
+
 /* For user TLB flushing (primarily for tests) */
 #define GRU_USER_FLUSH_TLB             _IOWR(GRU_IOCTL_NUM, 50, void *)
 
 
 #define CONTEXT_WINDOW_BYTES(th)        (GRU_GSEG_PAGESIZE * (th))
 #define THREAD_POINTER(p, th)          (p + GRU_GSEG_PAGESIZE * (th))
+#define GSEG_START(cb)                 ((void *)((unsigned long)(cb) & ~(GRU_GSEG_PAGESIZE - 1)))
+
+/*
+ * Statictics kept on a per-GTS basis.
+ */
+struct gts_statistics {
+       unsigned long   fmm_tlbdropin;
+       unsigned long   upm_tlbdropin;
+       unsigned long   context_stolen;
+};
+
+struct gru_get_gseg_statistics_req {
+       unsigned long           gseg;
+       struct gts_statistics   stats;
+};
 
 /*
  * Structure used to pass TLB flush parameters to the driver
 
        spin_unlock(&blade->bs_lock);
 
        if (ngts) {
+               gts->ustats.context_stolen++;
                ngts->ts_steal_jiffies = jiffies;
                gru_unload_context(ngts, is_kernel_context(ngts) ? 0 : 1);
                gts_stolen(ngts, blade);
 
 #include <linux/wait.h>
 #include <linux/mmu_notifier.h>
 #include "gru.h"
+#include "grulib.h"
 #include "gruhandles.h"
 
 extern struct gru_stats_s gru_stats;
                                                          allocated CB */
        int                     ts_data_valid;  /* Indicates if ts_gdata has
                                                   valid data */
+       struct gts_statistics   ustats;         /* User statistics */
        unsigned long           ts_gdata[0];    /* save area for GRU data (CB,
                                                   DS, CBE) */
 };
 extern int gru_kservices_init(void);
 extern void gru_kservices_exit(void);
 extern int gru_dump_chiplet_request(unsigned long arg);
+extern long gru_get_gseg_statistics(unsigned long arg);
 extern irqreturn_t gru_intr(int irq, void *dev_id);
 extern int gru_handle_user_call_os(unsigned long address);
 extern int gru_user_flush_tlb(unsigned long arg);