]> xenbits.xensource.com Git - people/dariof/xen.git/commitdiff
gnttab: make resource limits per domain
authorJuergen Gross <jgross@suse.com>
Wed, 4 Oct 2017 12:24:13 +0000 (14:24 +0200)
committerJan Beulich <jbeulich@suse.com>
Wed, 4 Oct 2017 12:24:13 +0000 (14:24 +0200)
Instead of using the same global resource limits of grant tables (max.
number of grant frames, max. number of maptrack frames) for all domains
make these limits per domain. Set those per-domain limits in
grant_table_set_limits(). The global settings are serving as an upper
boundary now which must not be exceeded by a per-domain value. The
default of max_grant_frames is set to the maximum default xl will use.

While updating the semantics of the boot parameters remove the
documentation of the no longer existing gnttab_max_nr_frames and
correct the default gnttab_max_maptrack_frames uses.

Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com> [non-ARM parts]
Acked-by: Julien Grall <julien.grall@arm.com>
docs/misc/xen-command-line.markdown
xen/arch/arm/domain_build.c
xen/common/compat/grant_table.c
xen/common/grant_table.c
xen/include/asm-arm/grant_table.h
xen/include/asm-x86/grant_table.h
xen/include/xen/grant_table.h

index 9797c8db2d3cfaef8d9113d8fde31a6fc9b1a67a..9b6cd8e9d0e3f225a6d2b443b0d31b1d228412e2 100644 (file)
@@ -875,27 +875,28 @@ Specify which console gdbstub should use. See **console**.
 ### gnttab\_max\_frames
 > `= <integer>`
 
-> Default: `32`
+> Default: `64`
+
+> Can be modified at runtime
 
 Specify the maximum number of frames which any domain may use as part
-of its grant table.
+of its grant table. This value is an upper boundary of the per-domain
+value settable via Xen tools.
+
+Dom0 is using this value for sizing its grant table.
 
 ### gnttab\_max\_maptrack\_frames
 > `= <integer>`
 
-> Default: `8 * gnttab_max_frames`
-
-Specify the maximum number of frames to use as part of a domains
-maptrack array.
+> Default: `1024`
 
-### gnttab\_max\_nr\_frames
-> `= <integer>`
+> Can be modified at runtime
 
-*Deprecated*
-Use **gnttab\_max\_frames** and **gnttab\_max\_maptrack\_frames** instead.
+Specify the maximum number of frames to use as part of a domains
+maptrack array. This value is an upper boundary of the per-domain
+value settable via Xen tools.
 
-Specify the maximum number of frames per grant table operation and the
-maximum number of maptrack frames domain.
+Dom0 is using this value for sizing its maptrack table.
 
 ### guest\_loglvl
 > `= <level>[/<rate-limited level>]` where level is `none | error | warning | info | debug | all`
index c34238ec1bc4adec66261364d2df3f32c27be7ed..3723dc3f7876864154831b2a2fd98ff4ccc458bb 100644 (file)
@@ -2095,11 +2095,7 @@ static void __init find_gnttab_region(struct domain *d,
      * enough space for a large grant table
      */
     kinfo->gnttab_start = __pa(_stext);
-    kinfo->gnttab_size = (_etext - _stext) & PAGE_MASK;
-
-    /* Make sure the grant table will fit in the region */
-    if ( (kinfo->gnttab_size >> PAGE_SHIFT) < max_grant_frames )
-        panic("Cannot find a space for the grant table region\n");
+    kinfo->gnttab_size = gnttab_dom0_frames() << PAGE_SHIFT;
 
 #ifdef CONFIG_ARM_32
     /*
index cce3ff0b9a90a53978bff6d2e4316fd4487a36b2..ff1d678f01c7b74dcf3b40d101f679b6d8ccd55e 100644 (file)
@@ -157,21 +157,14 @@ int compat_grant_table_op(unsigned int cmd,
                 unsigned int max_frame_list_size_in_page =
                     (COMPAT_ARG_XLAT_SIZE - sizeof(*nat.setup)) /
                     sizeof(*nat.setup->frame_list.p);
-                if ( max_frame_list_size_in_page < max_grant_frames )
-                {
-                    gdprintk(XENLOG_WARNING,
-                             "max_grant_frames is too large (%u,%u)\n",
-                             max_grant_frames, max_frame_list_size_in_page);
-                    rc = -EINVAL;
-                }
-                else
-                {
+
 #define XLAT_gnttab_setup_table_HNDL_frame_list(_d_, _s_) \
-                    set_xen_guest_handle((_d_)->frame_list, (unsigned long *)(nat.setup + 1))
-                    XLAT_gnttab_setup_table(nat.setup, &cmp.setup);
+                set_xen_guest_handle((_d_)->frame_list, (unsigned long *)(nat.setup + 1))
+                XLAT_gnttab_setup_table(nat.setup, &cmp.setup);
 #undef XLAT_gnttab_setup_table_HNDL_frame_list
-                    rc = gnttab_setup_table(guest_handle_cast(nat.uop, gnttab_setup_table_t), 1);
-                }
+                rc = gnttab_setup_table(guest_handle_cast(nat.uop,
+                                                          gnttab_setup_table_t),
+                                        1, max_frame_list_size_in_page);
             }
             ASSERT(rc <= 0);
             if ( rc == 0 )
@@ -294,16 +287,6 @@ int compat_grant_table_op(unsigned int cmd,
                 rc = -EFAULT;
                 break;
             }
-            if ( max_frame_list_size_in_pages <
-                 grant_to_status_frames(max_grant_frames) )
-            {
-                gdprintk(XENLOG_WARNING,
-                         "grant_to_status_frames(max_grant_frames) is too large (%u,%u)\n",
-                         grant_to_status_frames(max_grant_frames),
-                         max_frame_list_size_in_pages);
-                rc = -EINVAL;
-                break;
-            }
 
 #define XLAT_gnttab_get_status_frames_HNDL_frame_list(_d_, _s_) \
             set_xen_guest_handle((_d_)->frame_list, (uint64_t *)(nat.get_status + 1))
@@ -312,7 +295,7 @@ int compat_grant_table_op(unsigned int cmd,
 
             rc = gnttab_get_status_frames(
                 guest_handle_cast(nat.uop, gnttab_get_status_frames_t),
-                count);
+                count, max_frame_list_size_in_pages);
             if ( rc >= 0 )
             {
 #define XLAT_gnttab_get_status_frames_HNDL_frame_list(_d_, _s_) \
index 5abf79d1dbb7e01b74ff9558a61f03bf0fcc354f..6d20b17739a2dc93ff78ff7d6bf07d78871ca7e9 100644 (file)
@@ -54,6 +54,9 @@ struct grant_table {
      * what version to use yet.
      */
     unsigned int          gt_version;
+    /* Resource limits of the domain. */
+    unsigned int          max_grant_frames;
+    unsigned int          max_maptrack_frames;
     /* Table size. Number of frames shared with guest */
     unsigned int          nr_grant_frames;
     /* Number of grant status frames shared with guest (for version 2) */
@@ -78,23 +81,18 @@ struct grant_table {
 
 #ifndef DEFAULT_MAX_NR_GRANT_FRAMES /* to allow arch to override */
 /* Default maximum size of a grant table. [POLICY] */
-#define DEFAULT_MAX_NR_GRANT_FRAMES   32
+#define DEFAULT_MAX_NR_GRANT_FRAMES   64
 #endif
 
-unsigned int __read_mostly max_grant_frames;
-integer_param("gnttab_max_frames", max_grant_frames);
+static unsigned int __read_mostly max_grant_frames =
+                                               DEFAULT_MAX_NR_GRANT_FRAMES;
+integer_runtime_param("gnttab_max_frames", max_grant_frames);
 
-/* The maximum number of grant mappings is defined as a multiplier of the
- * maximum number of grant table entries. This defines the multiplier used.
- * Pretty arbitrary. [POLICY]
- * As gnttab_max_nr_frames has been deprecated, this multiplier is deprecated too.
- * New options allow to set max_maptrack_frames and
- * map_grant_table_frames independently.
- */
 #define DEFAULT_MAX_MAPTRACK_FRAMES 1024
 
-static unsigned int __read_mostly max_maptrack_frames;
-integer_param("gnttab_max_maptrack_frames", max_maptrack_frames);
+static unsigned int __read_mostly max_maptrack_frames =
+                                               DEFAULT_MAX_MAPTRACK_FRAMES;
+integer_runtime_param("gnttab_max_maptrack_frames", max_maptrack_frames);
 
 /*
  * Note that the three values below are effectively part of the ABI, even if
@@ -290,8 +288,8 @@ num_act_frames_from_sha_frames(const unsigned int num)
     return DIV_ROUND_UP(num * sha_per_page, ACGNT_PER_PAGE);
 }
 
-#define max_nr_active_grant_frames \
-    num_act_frames_from_sha_frames(max_grant_frames)
+#define max_nr_active_grant_frames(gt) \
+    num_act_frames_from_sha_frames((gt)->max_grant_frames)
 
 static inline unsigned int
 nr_active_grant_frames(struct grant_table *gt)
@@ -530,7 +528,7 @@ get_maptrack_handle(
      * out of memory, try stealing an entry from another VCPU (in case the
      * guest isn't mapping across its VCPUs evenly).
      */
-    if ( nr_maptrack_frames(lgt) < max_maptrack_frames )
+    if ( nr_maptrack_frames(lgt) < lgt->max_maptrack_frames )
         new_mt = alloc_xenheap_page();
 
     if ( !new_mt )
@@ -1677,7 +1675,7 @@ gnttab_grow_table(struct domain *d, unsigned int req_nr_frames)
 
     if ( req_nr_frames < INITIAL_NR_GRANT_FRAMES )
         req_nr_frames = INITIAL_NR_GRANT_FRAMES;
-    ASSERT(req_nr_frames <= max_grant_frames);
+    ASSERT(req_nr_frames <= gt->max_grant_frames);
 
     gdprintk(XENLOG_INFO,
             "Expanding dom (%d) grant table from (%d) to (%d) frames.\n",
@@ -1735,7 +1733,8 @@ active_alloc_failed:
 }
 
 static int
-grant_table_init(struct domain *d, struct grant_table *gt)
+grant_table_init(struct domain *d, struct grant_table *gt,
+                 unsigned int grant_frames, unsigned int maptrack_frames)
 {
     int ret = -ENOMEM;
 
@@ -1747,25 +1746,31 @@ grant_table_init(struct domain *d, struct grant_table *gt)
         goto out_no_cleanup;
     }
 
+    gt->max_grant_frames = grant_frames;
+    gt->max_maptrack_frames = maptrack_frames;
+
     /* Active grant table. */
     gt->active = xzalloc_array(struct active_grant_entry *,
-                               max_nr_active_grant_frames);
+                               max_nr_active_grant_frames(gt));
     if ( gt->active == NULL )
         goto out;
 
     /* Tracking of mapped foreign frames table */
-    gt->maptrack = vzalloc(max_maptrack_frames * sizeof(*gt->maptrack));
-    if ( gt->maptrack == NULL )
-        goto out;
+    if ( gt->max_maptrack_frames )
+    {
+        gt->maptrack = vzalloc(gt->max_maptrack_frames * sizeof(*gt->maptrack));
+        if ( gt->maptrack == NULL )
+            goto out;
+    }
 
     /* Shared grant table. */
-    gt->shared_raw = xzalloc_array(void *, max_grant_frames);
+    gt->shared_raw = xzalloc_array(void *, gt->max_grant_frames);
     if ( gt->shared_raw == NULL )
         goto out;
 
     /* Status pages for grant table - for version 2 */
     gt->status = xzalloc_array(grant_status_t *,
-                               grant_to_status_frames(max_grant_frames));
+                               grant_to_status_frames(gt->max_grant_frames));
     if ( gt->status == NULL )
         goto out;
 
@@ -1798,7 +1803,8 @@ grant_table_init(struct domain *d, struct grant_table *gt)
 
 static long
 gnttab_setup_table(
-    XEN_GUEST_HANDLE_PARAM(gnttab_setup_table_t) uop, unsigned int count)
+    XEN_GUEST_HANDLE_PARAM(gnttab_setup_table_t) uop, unsigned int count,
+    unsigned int limit_max)
 {
     struct vcpu *curr = current;
     struct gnttab_setup_table op;
@@ -1812,15 +1818,6 @@ gnttab_setup_table(
     if ( unlikely(copy_from_guest(&op, uop, 1)) )
         return -EFAULT;
 
-    if ( unlikely(op.nr_frames > max_grant_frames) )
-    {
-        gdprintk(XENLOG_INFO, "Xen only supports up to %d grant-table frames"
-                " per domain.\n",
-                max_grant_frames);
-        op.status = GNTST_general_error;
-        goto out;
-    }
-
     if ( !guest_handle_okay(op.frame_list, op.nr_frames) )
         return -EFAULT;
 
@@ -1840,6 +1837,21 @@ gnttab_setup_table(
     gt = d->grant_table;
     grant_write_lock(gt);
 
+    if ( unlikely(op.nr_frames > gt->max_grant_frames) )
+    {
+        gdprintk(XENLOG_INFO, "d%d is limited to %u grant-table frames\n",
+                d->domain_id, gt->max_grant_frames);
+        op.status = GNTST_general_error;
+        goto unlock;
+    }
+    if ( unlikely(limit_max < op.nr_frames) )
+    {
+        gdprintk(XENLOG_WARNING, "nr_frames for d%d is too large (%u,%u)\n",
+                 d->domain_id, op.nr_frames, limit_max);
+        op.status = GNTST_general_error;
+        goto unlock;
+    }
+
     if ( gt->gt_version == 0 )
         gt->gt_version = 1;
 
@@ -1849,8 +1861,9 @@ gnttab_setup_table(
          gnttab_grow_table(d, op.nr_frames) )
     {
         gdprintk(XENLOG_INFO,
-                 "Expand grant table to %u failed. Current: %u Max: %u\n",
-                 op.nr_frames, nr_grant_frames(gt), max_grant_frames);
+                 "Expand grant table of d%d to %u failed. Current: %u Max: %u\n",
+                 d->domain_id, op.nr_frames, nr_grant_frames(gt),
+                 gt->max_grant_frames);
         op.status = GNTST_general_error;
         goto unlock;
     }
@@ -1885,6 +1898,7 @@ gnttab_query_size(
 {
     struct gnttab_query_size op;
     struct domain *d;
+    struct grant_table *gt;
 
     if ( count != 1 )
         return -EINVAL;
@@ -1905,13 +1919,15 @@ gnttab_query_size(
         goto out;
     }
 
-    grant_read_lock(d->grant_table);
+    gt = d->grant_table;
 
-    op.nr_frames     = nr_grant_frames(d->grant_table);
-    op.max_nr_frames = max_grant_frames;
+    grant_read_lock(gt);
+
+    op.nr_frames     = nr_grant_frames(gt);
+    op.max_nr_frames = gt->max_grant_frames;
     op.status        = GNTST_okay;
 
-    grant_read_unlock(d->grant_table);
+    grant_read_unlock(gt);
 
  out:
     if ( d )
@@ -2986,7 +3002,7 @@ gnttab_set_version(XEN_GUEST_HANDLE_PARAM(gnttab_set_version_t) uop)
 
 static long
 gnttab_get_status_frames(XEN_GUEST_HANDLE_PARAM(gnttab_get_status_frames_t) uop,
-                         int count)
+                         unsigned int count, unsigned int limit_max)
 {
     gnttab_get_status_frames_t op;
     struct domain *d;
@@ -3026,9 +3042,19 @@ gnttab_get_status_frames(XEN_GUEST_HANDLE_PARAM(gnttab_get_status_frames_t) uop,
 
     if ( unlikely(op.nr_frames > nr_status_frames(gt)) )
     {
-        gdprintk(XENLOG_INFO, "Guest requested addresses for %d grant status "
-                 "frames, but only %d are available.\n",
-                 op.nr_frames, nr_status_frames(gt));
+        gdprintk(XENLOG_INFO, "Requested addresses of d%d for %u grant "
+                 "status frames, but has only %u\n",
+                 d->domain_id, op.nr_frames, nr_status_frames(gt));
+        op.status = GNTST_general_error;
+        goto unlock;
+    }
+
+    if ( unlikely(limit_max < grant_to_status_frames(op.nr_frames)) )
+    {
+        gdprintk(XENLOG_WARNING,
+                 "grant_to_status_frames(%u) for d%d is too large (%u,%u)\n",
+                 op.nr_frames, d->domain_id,
+                 grant_to_status_frames(op.nr_frames), limit_max);
         op.status = GNTST_general_error;
         goto unlock;
     }
@@ -3341,7 +3367,7 @@ do_grant_table_op(
 
     case GNTTABOP_setup_table:
         rc = gnttab_setup_table(
-            guest_handle_cast(uop, gnttab_setup_table_t), count);
+            guest_handle_cast(uop, gnttab_setup_table_t), count, UINT_MAX);
         ASSERT(rc <= 0);
         break;
 
@@ -3390,7 +3416,8 @@ do_grant_table_op(
 
     case GNTTABOP_get_status_frames:
         rc = gnttab_get_status_frames(
-            guest_handle_cast(uop, gnttab_get_status_frames_t), count);
+            guest_handle_cast(uop, gnttab_get_status_frames_t), count,
+                              UINT_MAX);
         break;
 
     case GNTTABOP_get_version:
@@ -3470,7 +3497,7 @@ grant_table_create(
 
     if ( d->domain_id == 0 )
     {
-        ret = grant_table_init(d, t);
+        ret = grant_table_init(d, t, gnttab_dom0_frames(), max_maptrack_frames);
     }
 
     return ret;
@@ -3671,11 +3698,15 @@ int grant_table_set_limits(struct domain *d, unsigned int grant_frames,
 {
     struct grant_table *gt = d->grant_table;
 
+    if ( grant_frames < INITIAL_NR_GRANT_FRAMES ||
+         grant_frames > max_grant_frames ||
+         maptrack_frames > max_maptrack_frames )
+        return -EINVAL;
     if ( !gt )
         return -ENOENT;
 
     /* Set limits. */
-    return grant_table_init(d, gt);
+    return grant_table_init(d, gt, grant_frames, maptrack_frames);
 }
 
 #ifdef CONFIG_HAS_MEM_SHARING
@@ -3747,7 +3778,7 @@ int gnttab_map_frame(struct domain *d, unsigned long idx, gfn_t gfn,
     }
     else
     {
-        if ( (idx >= nr_grant_frames(gt)) && (idx < max_grant_frames) )
+        if ( (idx >= nr_grant_frames(gt)) && (idx < gt->max_grant_frames) )
             gnttab_grow_table(d, idx + 1);
 
         if ( idx < nr_grant_frames(gt) )
@@ -3775,6 +3806,12 @@ static void gnttab_usage_print(struct domain *rd)
 
     grant_read_lock(gt);
 
+    printk("grant-table for remote d%d (v%u)\n"
+           "  %u frames (%u max), %u maptrack frames (%u max)\n",
+           rd->domain_id, gt->gt_version,
+           nr_grant_frames(gt), gt->max_grant_frames,
+           nr_maptrack_frames(gt), gt->max_maptrack_frames);
+
     for ( ref = 0; ref != nr_grant_entries(gt); ref++ )
     {
         struct active_grant_entry *act;
@@ -3802,12 +3839,7 @@ static void gnttab_usage_print(struct domain *rd)
             status = status_entry(gt, ref);
         }
 
-        if ( first )
-        {
-            printk("grant-table for remote domain:%5d (v%d)\n",
-                   rd->domain_id, gt->gt_version);
-            first = 0;
-        }
+        first = 0;
 
         /*      [0xXXX]  ddddd 0xXXXXXX 0xXXXXXXXX      ddddd 0xXXXXXX 0xXX */
         printk("[0x%03x]  %5d 0x%06lx 0x%08x      %5d 0x%06"PRIx64" 0x%02x\n",
@@ -3819,8 +3851,7 @@ static void gnttab_usage_print(struct domain *rd)
     grant_read_unlock(gt);
 
     if ( first )
-        printk("grant-table for remote domain:%5d ... "
-               "no active grant table entries\n", rd->domain_id);
+        printk("no active grant table entries\n");
 }
 
 static void gnttab_usage_print_all(unsigned char key)
@@ -3834,20 +3865,17 @@ static void gnttab_usage_print_all(unsigned char key)
 
 static int __init gnttab_usage_init(void)
 {
-    BUILD_BUG_ON(DEFAULT_MAX_MAPTRACK_FRAMES < DEFAULT_MAX_NR_GRANT_FRAMES);
-
-    if ( !max_grant_frames )
-        max_grant_frames = DEFAULT_MAX_NR_GRANT_FRAMES;
-
-    if ( !max_maptrack_frames )
-        max_maptrack_frames = DEFAULT_MAX_MAPTRACK_FRAMES;
-
     register_keyhandler('g', gnttab_usage_print_all,
                         "print grant table usage", 1);
     return 0;
 }
 __initcall(gnttab_usage_init);
 
+unsigned int __init gnttab_dom0_frames(void)
+{
+    return min(max_grant_frames, gnttab_dom0_max());
+}
+
 /*
  * Local variables:
  * mode: C
index 30db2d161615fadf4db100d05e78519fab582f0b..0dfdc5577fe5c9e7f9a4dda4848813a83ba6e7d6 100644 (file)
@@ -2,9 +2,11 @@
 #define __ASM_GRANT_TABLE_H__
 
 #include <xen/grant_table.h>
+#include <xen/kernel.h>
+#include <xen/pfn.h>
 #include <xen/sched.h>
 
-#define INITIAL_NR_GRANT_FRAMES 4
+#define INITIAL_NR_GRANT_FRAMES 1U
 
 struct grant_table_arch {
     gfn_t *gfn;
@@ -26,9 +28,21 @@ static inline int replace_grant_supported(void)
     return 1;
 }
 
+/*
+ * The region used by Xen on the memory will never be mapped in DOM0
+ * memory layout. Therefore it can be used for the grant table.
+ *
+ * Only use the text section as it's always present and will contain
+ * enough space for a large grant table
+ */
+static inline unsigned int gnttab_dom0_max(void)
+{
+    return PFN_DOWN(_etext - _stext);
+}
+
 #define gnttab_init_arch(gt)                                             \
 ({                                                                       \
-    (gt)->arch.gfn = xzalloc_array(gfn_t, max_grant_frames);             \
+    (gt)->arch.gfn = xzalloc_array(gfn_t, (gt)->max_grant_frames);       \
     ( (gt)->arch.gfn ? 0 : -ENOMEM );                                    \
 })
 
@@ -52,7 +66,7 @@ static inline int replace_grant_supported(void)
 
 #define gnttab_shared_gmfn(d, t, i)                                      \
     ( ((i >= nr_grant_frames(t)) &&                                      \
-       (i < max_grant_frames)) ? 0 : gfn_x(t->arch.gfn[i]))
+       (i < (t)->max_grant_frames))? 0 : gfn_x((t)->arch.gfn[i]))
 
 #define gnttab_need_iommu_mapping(d)                    \
     (is_domain_direct_mapped(d) && need_iommu(d))
index 1b93c5720d1a4f1035913dc96093f51fb9c745b5..d9157e441712a1ade91eb5ad68067aa0c56f0430 100644 (file)
@@ -12,7 +12,7 @@
 #include <asm/hvm/grant_table.h>
 #include <asm/pv/grant_table.h>
 
-#define INITIAL_NR_GRANT_FRAMES 4
+#define INITIAL_NR_GRANT_FRAMES 1U
 
 struct grant_table_arch {
 };
@@ -39,6 +39,11 @@ static inline int replace_grant_host_mapping(uint64_t addr, unsigned long frame,
     return replace_grant_pv_mapping(addr, frame, new_addr, flags);
 }
 
+static inline unsigned int gnttab_dom0_max(void)
+{
+    return UINT_MAX;
+}
+
 #define gnttab_init_arch(gt) 0
 #define gnttab_destroy_arch(gt) do {} while ( 0 )
 #define gnttab_set_frame_gfn(gt, idx, gfn) do {} while ( 0 )
index d2bd2416c4befb7a3e662847fba9d33ed9062818..b3a95fda5860a5171da3723b1f2bc7a191f3d218 100644 (file)
@@ -31,9 +31,6 @@
 
 struct grant_table;
 
-/* The maximum size of a grant table. */
-extern unsigned int max_grant_frames;
-
 /* Create/destroy per-domain grant table context. */
 int grant_table_create(
     struct domain *d);
@@ -59,4 +56,6 @@ int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref,
 int gnttab_map_frame(struct domain *d, unsigned long idx, gfn_t gfn,
                      mfn_t *mfn);
 
+unsigned int gnttab_dom0_frames(void);
+
 #endif /* __XEN_GRANT_TABLE_H__ */